7
votes

I'm creating a mesh with a custom shader. Within the vertex shader I'm modifying the original position of the geometry vertices. Then I need to access to this new vertices position from outside the shader, how can I accomplish this?

1

1 Answers

8
votes

In lieu of transform feedback (which WebGL 1.0 does not support), you will have to use a passthrough fragment shader and floating-point texture (this requires loading the extension OES_texture_float). That is the only approach to generate a vertex buffer on the GPU in WebGL. WebGL does not support pixel buffer objects either, so reading the output data back is going to be very inefficient.

Nevertheless, here is how you can accomplish this:

This will be a rough overview focusing on OpenGL rather than anything Three.js specific.

First, encode your vertex array this way (add a 4th component for index):

Vec4 pos_idx  :  xyz = Vertex Position,  w = Vertex Index (0.0 through NumVerts-1.0)

Storing the vertex index as the w component is necessary because OpenGL ES 2.0 (WebGL 1.0) does not support gl_VertexID.

Next, you need a 2D floating-point texture:

MaxTexSize = Query GL_MAX_TEXTURE_SIZE

Width  = MaxTexSize;
Height = min (NumVerts / MaxTexSize, 1);

Create an RGBA floating-point texture with those dimensions and use it as FBO color attachment 0.

Vertex Shader:

#version 100

attribute vec4 pos_idx;

uniform int width;  // Width of floating-point texture
uniform int height; // Height of floating-point texture

varying vec4 vtx_out;

void main (void)
{
  float idx = pos_idx.w;

  // Position this vertex so that it occupies a unique pixel
  vec2 xy_idx = vec2 (float ((int (idx) % width)) / float (width),
                      floor (idx / float (width)) / float (height)) * vec2 (2.0) - vec2 (1.0);
  gl_Position = vec4 (xy_idx, 0.0f, 1.0f);


  //
  // Do all of your per-vertex calculations here, and output to vtx_out.xyz
  //


  // Store the index in the W component
  vtx_out.w = idx;
}

Passthrough Fragment Shader:

#version 100

varying vec4 vtx_out;

void main (void)
{
  gl_FragData [0] = vtx_out;
}

Draw and Read Back:

// Draw your entire vertex array for processing (as `GL_POINTS`)
glDrawArrays (GL_POINTS, 0, NumVerts);

// Bind the FBO's color attachment 0 to `GL_TEXTURE_2D`

// Read the texture back and store its results in an array `verts`
glGetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, verts);