我使用drawArrays(gl.TRIANGLES,...)在画布上绘制了一些曲面的顶点。我需要为特定的摄像机视点绘制这些曲面,因此所有3d点都投影到2d,我使用toDataUrl下载最终图像。这是下载的图像:
我之后使用gl.readPixels来检索每个像素的数据。
对于所有边缘顶点,我都有法线信息。就像我得到2d图像中每个像素的颜色一样,我想得到2d图像的每个像素的法线。由于我只在边缘顶点处有法线,所以我决定渲染法线的方式与渲染上面的图像相同,并决定使用gl.readpixels。这不起作用。以下是相关代码:
这是调用drawOverlayTriangeNormals的函数。 drawOverlayTriangles函数(在这篇文章中不可见)用于生成上面显示的图像。
//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);
}
这是drawOverlayTrianglesNormals函数:
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);
}
以下是相关的顶点着色器代码:
void main(void) {
gl_PointSize = aPointSize;
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
position_1 = gl_Position;
vColor = aVertexColor;
vIsDepth = aIsDepth;
vIsNormal = aIsNormal;
vHasTexture = aHasTexture;
normals = uNMatrix*vec4(aNormal,1.0);
if (aHasTexture > 0.5)
vTextureCoord = aTextureCoord;
}
片段着色器:
if (vIsNormal > 0.5)
{
gl_FragColor = vec4(normals.xyz*0.5+0.5,1);
}
}
现在我的输出是灰度相同的图像。我不确定出了什么问题。我觉得这种方法很有意义,但似乎有点圆了。
答案 0 :(得分:0)
我不完全确定我明白你要做的是什么,但似乎你只是想要能够访问法线来计算灯光效果,所以让我试着回答一下。
请勿使用gl.readPixels()!这主要用于点击交互和内容,或修改少量像素。使用它必须非常低效,因为您必须绘制像素,然后读取它们,然后在计算适当的光照后重绘它们。 WebGL的精彩之处在于它允许您从头开始执行所有这些操作:片段着色器将插入它给出的信息,以便在两个相邻顶点之间平滑地绘制效果。
大多数照明取决于将表面法线与光线方向进行比较(正如您所理解的那样,根据您的评论判断)。请参阅Phong shading。
现在,您提到您想要所有渲染点的法线,而不仅仅是顶点。但是顶点的法线将与曲面上每个点的法线相同,因此您甚至不需要比顶点法线更多的法线。这是因为所有WebGL都知道如何绘制三角形(我相信),它们是平面或平面的表面。并且由于飞机上的每个点与任何其他点具有相同的法线,因此您只需要一个法线来了解所有法线!
因为看起来你要绘制的所有东西都是圆柱和矩形棱镜,所以指定你创建的对象的法线应该很简单。矩形棱镜的法线是微不足道的,但是圆柱体的法线也是如此:法线平行于从圆柱轴到表面的直线。
由于WebGL的片段着色器会插入您在相邻顶点之间传递的任何varying
变量,您可以告诉它在顶点之间平滑地插入这些法线,以实现在Phong shadin页面中看到的平滑光照! :d