悬垂检测着色器 - 如何返回顶点的坐标?

时间:2017-05-21 12:17:15

标签: javascript three.js webgl webgl2

我正在尝试使用three.js在浏览器中编写支持生成应用程序,我尝试了很多方法并且所有方法都很慢,所以现在我决定让着色器计算悬空位置并且我的程序构建支持那些要点。

突出检测着色器输出: overhang detection

现在问题是我无法弄清楚如何将这些区域以红色方式返回到CPU /主JavaScript应用程序以生成对这些点的简单支持, 我在这里读到有关涉及FBO的GPU CPU方法,但无法理解这一点,有没有办法让红色区域坐标回到CPU?

我还可以在顶点着色器中计算这个以将非悬垂顶点的位置更新为0,0,0,但问题是三个JavaScript中的顶点位置不会以这种方式更新,如果有的话在顶点着色器执行后得到更新顶点位置的某种方法可能是一种解决方案。

也许改变反馈?我怎样才能使用来自three.js的转换反馈?

2 个答案:

答案 0 :(得分:0)

如果你想获得渲染的图像(就像你在问题中链接的那个),你可以使用三个readPixels readRenderTargetPixels的包装器。这将为您提供图像像素值作为数组,您可以迭代它并找到红色区域。此外,由于您的片段着色器似乎做了相当多的二元决策(黑色或红色),您可以使用其他通道来存储其他信息,例如:在顶点着色器中:

// ...
varying vec3 position;
// ...
void main(void) {
    // ...
    position = gl_Position.xyz / gl_Position.w;
}

在片段着色器中:

// ...
varying highp vec3 position;
// ...
void main(void) {
    // ...
    gl_FragColor.xyz = 0.5 * (position + 1.0); // position'll be in (-1, 1) range, where as gl_FragColor's clamped to (0, 1)
    gl_FragColor.w = isOverhang ? 1.0 : 0.0;
}

然后在JS代码中:

// ...
const pixelBuffer = new Uint8Array(4 * w * h);
renderer.readRenderTargetPixels(renderTarget, 0, 0, w, h, pixelBuffer);
for (let y = 0, offset = 0; y < h; ++y) {
    for (let x = 0; x < w; ++x, offset += 4) {

         // does pixel correspond to overhang area?
         if (pixelBuffer[offset + 3] > 0) {
             const posX = 2 * pixelBuffer[offset] / 255 - 1;
             const posY = 2 * pixelBuffer[offset + 1] / 255 - 1;
             const posZ = 2 * pixelBuffer[offset + 2] / 255 - 1;
             // ...
         }
    }
}

但是,8位精度可能不足以满足您的需要。在这种情况下,您可以使用FLOATHALF_FLOAT渲染目标(如果浏览器支持这些目标)。

您也可以尝试GPGPU方法。基本上,大多数情况下,它使用片段着色器来计算某些值,然后将其存储在纹理中(通常,FLOATHALF_FLOAT)并读回CPU或采样在后续绘图中使用计算值。 WebGL中有很多关于GPGPU的信息,例如: this

关于转换反馈。是的,它专门用于在某个缓冲区中存储顶点着色器的结果,这些结果可以再次读回CPU(很少)或在GPU上重复使用,例如作为另一个甚至相同顶点着色器的输入。但TF仅在WebGL 2中可用。

答案 1 :(得分:0)

您可以使用其他FBO或转换反馈。使用变换反馈,唯一的问题是AFAICT,没有办法丢弃顶点,所以就像你提到的那样,在这种情况下你能做的最好就是为非重叠顶点写一些特殊的值。

要使用FBO,您可以制作浮点纹理并检查是否可以渲染它。在WebGL1中,这意味着启用浮点纹理,将其绑定到帧缓冲区并调用checkFramebufferStatus。在WebGL 2中,它意味着检查并启用EXT_color_buffer_float(并仍然调用checkFramebufferStatus)

然后使用计数器[0,1,2,3,4,5,6等]制作一个缓冲区来生成gl_Position,该// WebGL2 varying uint count; uniform uint2 resolutionOfFBO; // compute output pixel uint x = count % resolutinOfFBO.x; uint y = count / resolutionOfFBO.x; // set gl_Position so we'll write to that output pixel gl_Position = vec4((vec2(x, y) + .5) / resolutionOfFBO, 0, 1); 将写入FBO中的下一个像素。

POINTS

将要写入的数据传递给变量,并在片段着色器中写入该数据。然后使用gl.readPixels进行渲染。

然后,您可以使用[1, 2, 3]

重新读取数据

对于这个问题,解释变换反馈似乎有点长,但这只是一个简单的例子:输入为[2, 4, 6],输出为function main() { const gl = document.createElement("canvas").getContext("webgl2"); const vs = `#version 300 es in float in_value; out float out_value; void main() { out_value = in_value * 2.; } `; const fs = `#version 300 es precision mediump float; layout (location = 0) out vec4 dummy; void main() { dummy = vec4(1); } `; const prog = createProgram(gl, [vs, fs], ["out_value"]); const inLoc = gl.getAttribLocation(prog, 'in_value'); const outLoc = 0; const numVaryings = gl.getProgramParameter(prog, gl.TRANSFORM_FEEDBACK_VARYINGS); const srcBuffer1 = createBuffer(gl, new Float32Array([1, 2, 3])); const srcVAO1 = createVAO(gl, srcBuffer1, inLoc); const dstBuffer = createBuffer(gl, Float32Array.BYTES_PER_ELEMENT * 3); const srcVAO2 = createVAO(gl, dstBuffer, inLoc); const tf = gl.createTransformFeedback(); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf); gl.useProgram(prog); gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dstBuffer); // this binds the default (id = 0) TRANSFORM_FEEBACK buffer gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); // This line is onky because of a bug in Chrome gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); runFeedback(gl, prog, srcVAO1, tf); checkGLError(gl); const result = new Float32Array(3); gl.bindBuffer(gl.ARRAY_BUFFER, dstBuffer); gl.getBufferSubData(gl.ARRAY_BUFFER, 0, result); log(result); } main(); function runFeedback(gl, prog, srcVAO, tf, dstBufferInfo) { gl.enable(gl.RASTERIZER_DISCARD); gl.useProgram(prog); gl.bindVertexArray(srcVAO); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf); gl.beginTransformFeedback(gl.TRIANGLES); gl.drawArrays(gl.TRIANGLES, 0, 3); gl.endTransformFeedback(); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); gl.disable(gl.RASTERIZER_DISCARD); } function checkGLError(gl) { const err = gl.getError(); if (err) { log("GL ERROR:", err); } } function createShader(gl, shaderSource, shaderType) { var shader = gl.createShader(shaderType); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!compiled) { console.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } function createProgram(gl, shaderSources, outputs) { const shaderTypes = [gl.VERTEX_SHADER, gl.FRAGMENT_SHADER]; const program = gl.createProgram(); shaderSources.forEach(function(shaderSrc, ndx) { gl.attachShader(program, createShader(gl, shaderSrc, shaderTypes[ndx])); }); if (outputs) { gl.transformFeedbackVaryings(program, outputs, gl.SEPARATE_ATTRIBS); } gl.linkProgram(program); var linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { console.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); return null; } return program; } function createBuffer(gl, dataOrSize) { const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, dataOrSize, gl.STATIC_DRAW); return buf; } function createVAO(gl, buf, inLoc) { const vao = gl.createVertexArray(); gl.bindVertexArray(vao); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.enableVertexAttribArray(inLoc); gl.vertexAttribPointer(inLoc, 1, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, null); // this is not needed gl.bindVertexArray(null); return vao; } function log(...args) { const elem = document.createElement("pre"); elem.textContent = [...args].join(' '); document.body.appendChild(elem); }

&#13;
&#13;
gl.transformFeedbackVaryings
&#13;
&#13;
&#13;

简短的解释是使用变换反馈将顶点着色器的输出变化写入一个或多个缓冲区。

要做到这一点,你必须在链接时告诉你的着色器程序你的输出是gl.bindBufferBase

然后创建变换反馈对象。变换反馈对象与顶点数组对象非常相似,除了输出而不是输入。您可以通过为每个输出调用gl.vertexAttribPointer来指定输出,就像您为顶点数组对象上的每个输入调用gl.enable(gl.RASTERIZER_DISCARD); 一样。

要实际生成输出,您可能希望告诉WebGL不要运行片段着色器

gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.TRIANGLES);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.endTransformFeedback();

然后绑定变换反馈对象,打开变换反馈并绘制

position0, normal0, position1, normal1, position2, normal2

链接程序时,您可以选择单独或交错的属性。使用单独的属性可以转到不同的缓冲区,但是您可以写入多少属性(min至少为4)。交错的所有输出都被写入但它们是交错的。例如,如果您正在写两个位置和法线,那么输出将是

<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $("#login").click(function(){
          alert('asdf');
            var page = "login"; 
            var username = $("#username").val().trim();
            var password = $("#password").val().trim();
            $.ajax({
                    url:'pages/content/input_query.php',
                    type:'post',
                    data:{page:page,username:username,password:password},
                    success:function(response){
                        var msg = "";
                        if(response ==1){
                            window.location = "index.php?pages=home";
                        }else{
                            $("#result").html(response);
                        }
                     }
                });

        });

    });
    </script>

全部到同一缓冲区。