在webgl中获取投影曲面的法线

在webgl中获取投影曲面的法线,webgl,projection,Webgl,Projection,我使用DrawArray(gl.三角形,…)在画布上绘制了一些曲面的顶点。我需要为特定的摄影机视点绘制这些曲面,因此所有3d点都投影到2d中,我使用toDataUrl下载最终图像。以下是下载的图像: 后来我使用gl.readPixels检索everypixel的数据 对于所有边顶点,我有法线的信息。就像我得到2d图像中每个像素的颜色一样,我想得到2d图像中每个像素的法线。因为我只在边顶点上有法线,所以我决定以与渲染上述图像相同的方式渲染法线,并决定使用gl.readpixels。这是行不通的。

我使用DrawArray(gl.三角形,…)在画布上绘制了一些曲面的顶点。我需要为特定的摄影机视点绘制这些曲面,因此所有3d点都投影到2d中,我使用toDataUrl下载最终图像。以下是下载的图像:

后来我使用gl.readPixels检索everypixel的数据

对于所有边顶点,我有法线的信息。就像我得到2d图像中每个像素的颜色一样,我想得到2d图像中每个像素的法线。因为我只在边顶点上有法线,所以我决定以与渲染上述图像相同的方式渲染法线,并决定使用gl.readpixels。这是行不通的。以下是相关代码:

这是调用drawOverlayTriangeNormals的函数。DrawOverlyTriangles函数(在本文中不可见)用于生成上面所示的图像

    //Saving BIM
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.vertexAttrib1f(shaderProgram.aIsDepth, 0.0);
    drawOverlayTriangles();
    saveBlob('element');

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.vertexAttrib1f(shaderProgram.aIsDepth, 0.0);
    drawOverlayTrianglesNormals();
    saveBlob('element');

    var pixels = new Uint8Array(glCanvas.width*glCanvas.height*4);
    gl.readPixels(0, 0, glCanvas.width, glCanvas.height, gl.RGBA, gl.UNSIGNED_BYTE,pixels);
    pixels = new Float32Array(pixels.buffer);   
}
这是DrawOverlayTriangelSnormals函数:

function drawOverlayTrianglesNormals()
{
if (overlay.numElements <= 0)
    return;

//Creating the matrix for normal transform
var normal_matrix = mat4.create();
var u_Normal_Matrix = mat4.create();
mat4.invert(normal_matrix,pMVMatrix);
mat4.transpose(u_Normal_Matrix,normal_matrix);  

gl.enable(gl.DEPTH_TEST);   

gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
gl.enableVertexAttribArray(shaderProgram.aVertexColor);
gl.enableVertexAttribArray(shaderProgram.aNormal);
gl.vertexAttrib1f(shaderProgram.aIsNormal, 1.0);

//Matrix upload
gl.uniformMatrix4fv(shaderProgram.uMVMatrix, false, pMVMatrix);
gl.uniformMatrix4fv(shaderProgram.uPMatrix, false, perspM);
gl.uniformMatrix4fv(shaderProgram.uNMatrix, false, u_Normal_Matrix);

//Create normals buffer
normals_buffer = gl.createBuffer();

for (var i = 0; i < overlay.numElements; i++) {
    // Upload overlay vertices                      
    gl.bindBuffer(gl.ARRAY_BUFFER, overlayVertices[i]);
    gl.vertexAttribPointer(shaderProgram.aVertexPosition, 3, gl.FLOAT, false, 0, 0);

    // Upload overlay colors
    gl.bindBuffer(gl.ARRAY_BUFFER, overlayTriangleColors[i]);       
    gl.vertexAttribPointer(shaderProgram.aVertexColor, 4, gl.FLOAT, false, 0, 0);

    var normal_vertex = [];

    //Upload Normals
        var normals_element = overlay.elementNormals[i];
        for( var j=0; j< overlay.elementNumVertices[i]; j++)
    {   
        var x = normals_element[3*j+0];
        var y = normals_element[3*j+1];
        var z = normals_element[3*j+2];
        var length = Math.sqrt(x*x + y*y + z*z);

        normal_vertex[3*j+0] = x/length;
        normal_vertex[3*j+1] = y/length;
        normal_vertex[3*j+2] = z/length;
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, normals_buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normal_vertex),gl.STATIC_DRAW);
    gl.vertexAttribPointer(shaderProgram.aNormal, 3, gl.FLOAT, false, 0, 0);    

    // Draw overlay
    gl.drawArrays(gl.TRIANGLES, 0, overlay.elementNumVertices[i]);
}

gl.disableVertexAttribArray(shaderProgram.aVertexPosition);
gl.disableVertexAttribArray(shaderProgram.aVertexColor);
gl.vertexAttrib1f(shaderProgram.aisdepth, 0.0);
}
片段着色器:

 if (vIsNormal > 0.5)
        {
            gl_FragColor = vec4(normals.xyz*0.5+0.5,1);

        }
    }

现在我的输出是相同的灰度图像。我不确定出了什么问题。我觉得这个方法很有道理,但似乎有点迂回。

我不完全确定我是否理解您的意图,但似乎您只是希望能够访问法线以计算照明效果,所以让我尝试回答这个问题

不要使用gl.readPixels()!这主要用于点击交互和填充,或者修改少量像素。使用这种方法肯定是非常低效的,因为您必须绘制像素,然后读取它们,然后在计算适当的照明后重新绘制它们。WebGL的奇妙之处在于,它允许您从一开始就执行所有这些操作:片段着色器将插入它提供的信息,以便在两个相邻顶点之间平滑绘制效果

大多数照明都依赖于将曲面法线与灯光方向进行比较(正如您所理解的,从您的一条评论判断)。看

现在,您提到需要所有渲染点的法线,而不仅仅是顶点。但是顶点的法线将与曲面上每个点的法线相同,因此您甚至不需要比顶点法线更多的东西。这是因为WebGL知道如何绘制三角形(我相信),三角形是平面或平面曲面。因为平面上的每个点都有和其他点相同的法线,所以你只需要一条法线就可以知道所有的法线

由于您试图绘制的似乎都是圆柱体和矩形棱柱体,因此指定您创建的对象的法线应该很简单。矩形棱柱体的法线很小,但圆柱体的法线也很小:法线平行于从圆柱体轴到曲面的直线


由于WebGL的片段着色器会插值在相邻顶点之间传递的任何
变化的
变量,您可以告诉它在顶点之间平滑插值这些法线,以实现Phong shadin页面中看到的平滑照明!:D

我不确定你想做什么,但是如果你想在片段着色器中有一个像素的法线,你可以通过你的顶点着色器用一个
variable
变量“赋予”它。我也不太明白。您可以调用“canvas.toDataURL”或
gl.readPixels
从画布中获取数据。至于渲染法线,是的,只需将它们从顶点着色器传递到片段着色器,然后使用它们进行渲染,如
gl_FragCoord=vec4(法线*0.5+0.5,1)我只需要所有渲染点的法线,而不仅仅是顶点。现在,颜色是通过检查法线并在gl_FragColor中使用它来手动完成的。如果我想要每个像素的法线,我应该渲染一个单独的图像,只使用法线值并使用readPixels吗?我在尝试了上面评论中提到的内容后编辑了我的帖子。我还包括了一些相关的代码
 if (vIsNormal > 0.5)
        {
            gl_FragColor = vec4(normals.xyz*0.5+0.5,1);

        }
    }