实例化绘制球体绕Y轴旋转时法向量导致光照异常问题
实例化绘制球体旋转后光照异常问题
我采用实例化绘制(instanced drawing)方式渲染球体,同时将模型视图矩阵绕Y轴旋转。
初始状态下显示正常:
但旋转到另一角度时显示效果异常:
初步判断是法向量相关问题,当前我从模型视图矩阵计算得到法向量矩阵后传入着色器,实现类Phong光照,相关代码如下:
顶点着色器代码
attribute vec4 a_position; attribute vec3 a_normal; attribute vec4 a_color; attribute vec2 a_coord; attribute mat4 a_matrix; uniform mat4 u_mv_matrix; uniform mat4 u_projection_matrix; uniform mat3 u_normal_matrix; varying vec4 v_position; varying vec3 v_normal; varying vec4 v_color; varying vec2 v_coord; void main() { vec4 transformedPosition = u_mv_matrix * a_matrix * a_position; v_position = transformedPosition; v_normal = u_normal_matrix * a_normal; v_color = a_color; v_coord = a_coord; gl_Position = u_projection_matrix * transformedPosition; }
片元着色器代码
uniform sampler2D u_sampler; varying vec4 v_position; varying vec3 v_normal; varying vec4 v_color; varying vec2 v_coord; void main() { vec3 lightPosition = vec3(0.0); // XXX // set diffuse and specular colors vec3 cDiffuse = (v_color * texture2D(u_sampler, v_coord)).rgb; vec3 cSpecular = vec3(0.3); // lighting calculations vec3 N = normalize(v_normal); vec3 L = normalize(lightPosition - v_position.xyz); vec3 E = normalize(-v_position.xyz); vec3 H = normalize(L + E); // Calculate coefficients. float phong = max(dot(N, L), 0.0); const float kMaterialShininess = 20.0; const float kNormalization = (kMaterialShininess + 8.0) / (3.14159265 * 8.0); float blinn = pow(max(dot(N, H), 0.0), kMaterialShininess) * kNormalization; // diffuse coefficient vec3 diffuse = phong * cDiffuse; // specular coefficient vec3 specular = blinn * cSpecular; gl_FragColor = vec4(diffuse + specular, 1); }
基础信息
- 运行环境:桌面端OpenGL 2.1、浏览器端WebGL
- 光源位置:在片元着色器中定义为
vec3(0) - 上层逻辑使用自研框架实现,网格构建、变换处理等能力经过验证运行正常
补充实现细节
网格构建逻辑
构建时传入单位矩阵,代码如下:
void Sphere::append(IndexedVertexBatch<XYZ.N.UV> &batch, const Matrix &matrix) const { float sectorStep = TWO_PI / sectorCount; float stackStep = PI / stackCount; for(int i = 0; i <= stackCount; ++i) { float stackAngle = HALF_PI - i * stackStep; float xy = radius * cosf(stackAngle); float z = radius * sinf(stackAngle); for(int j = 0; j <= sectorCount; ++j) { float sectorAngle = j * sectorStep; float x = xy * cosf(sectorAngle); float y = xy * sinf(sectorAngle); float nx = x / radius; float ny = y / radius; float nz = z / radius; float s = (float)j / sectorCount; float t = (float)i / stackCount; batch.addVertex(matrix.transformPoint(x, y, z), matrix.transformNormal(nx, ny, nz), glm::vec2(s, t)); } } for(int i = 0; i < stackCount; ++i) { float k1 = i * (sectorCount + 1); float k2 = k1 + sectorCount + 1; for(int j = 0; j < sectorCount; ++j, ++k1, ++k2) { if (i != 0) { if (frontFace == CCW) { batch.addIndices(k1, k1 + 1, k2); } else { batch.addIndices(k1, k2, k1 + 1); } } if (i != (stackCount - 1)) { if (frontFace == CCW) { batch.addIndices(k1 + 1, k2 + 1, k2); } else { batch.addIndices(k1 + 1, k2, k2 + 1); } } } } }
变换矩阵处理逻辑
camera.getMVMatrix() .setIdentity() .translate(0, -150, -600) .rotateY(clock()->getTime() * 0.5f); State() .setShader(shader) .setShaderMatrix<MV>(camera.getMVMatrix()) .setShaderMatrix<PROJECTION>(camera.getProjectionMatrix()) .setShaderMatrix<NORMAL>(camera.getNormalMatrix()) .apply();
问题更新
原使用的着色器光照部分逻辑存在错误,已更换为其他实现方式。最终的解决方法可在避免非均匀缩放的前提下,解决实例化绘制场景下的法向量问题。
内容的提问来源于stack exchange,提问作者Ariel Malka
相关产品推荐
相关产品推荐

