如何使用C++在OpenGL ES中实现xz平面的无限网格渲染
OpenGL ES XZ平面无限网格实现指南
该实现不需要生成任何静态网格顶点,完全通过着色器动态计算网格,性能和显示效果对标Blender、Maya的原生网格效果,适配OpenGL ES 3.0及以上版本。
渲染管线前置准备
- 无需准备网格顶点缓冲,仅需渲染一个覆盖全屏的三角形即可,比传统全屏四边形少一个顶点,性能更优
- 需开启深度测试
glEnable(GL_DEPTH_TEST)、混合glEnable(GL_BLEND),混合模式设置为glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - CPU端每帧需传入相机的视图投影逆矩阵、视图矩阵、网格尺寸参数、相机远裁剪面距离四个全局uniform变量
顶点着色器实现(GLSL ES 300)
#version 300 es precision highp float; out vec3 nearPoint; out vec3 farPoint; // CPU端每帧更新相机的视图投影逆矩阵 uniform mat4 invViewProj; // NDC坐标下的三个全屏三角形顶点 vec3 positions[3] = vec3[]( vec3(-1.0, -1.0, 0.0), vec3(3.0, -1.0, 0.0), vec3(-1.0, 3.0, 0.0) ); // NDC坐标转世界空间坐标 vec3 ndcToWorld(vec3 ndc) { vec4 pt = invViewProj * vec4(ndc, 1.0); pt /= pt.w; return pt.xyz; } void main() { vec3 pos = positions[gl_VertexID]; nearPoint = ndcToWorld(vec3(pos.x, pos.y, 0.0)); // 近裁剪面采样点 farPoint = ndcToWorld(vec3(pos.x, pos.y, 1.0)); // 远裁剪面采样点 gl_Position = vec4(pos, 1.0); }
片元着色器实现(GLSL ES 300)
#version 300 es precision highp float; in vec3 nearPoint; in vec3 farPoint; out vec4 fragColor; uniform mat4 view; uniform float mainGridStep; // 主网格间距,默认设为1即可 uniform float subGridStep; // 次网格间距,默认设为0.1即可 uniform vec3 gridColor; // 网格线颜色,默认深灰即可 uniform float cameraFar; // 相机远裁剪面距离 // 计算视锥射线与XZ平面(y=0)的交点参数t float computePlaneIntersectT() { return -nearPoint.y / (farPoint.y - nearPoint.y); } // 计算当前坐标对应网格线的透明度,自带抗锯齿效果 float calculateGridAlpha(vec2 xzCoord, float gridStep) { vec2 derivative = fwidth(xzCoord); vec2 grid = abs(fract(xzCoord / gridStep - 0.5) - 0.5) / derivative; float minLine = min(grid.x, grid.y); return 1.0 - min(minLine, 1.0); } void main() { float t = computePlaneIntersectT(); // 交点在相机后方或超出远裁剪面直接丢弃 if (t < 0.0 || t > 1.0) discard; // 计算世界空间下的交点坐标 vec3 worldPos = nearPoint + t * (farPoint - nearPoint); // 手动写入深度值,保证网格和场景其他物体遮挡关系正确 vec4 clipPos = view * vec4(worldPos, 1.0); float ndcDepth = (clipPos.z / clipPos.w); gl_FragDepth = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; // 叠加主次网格的透明度,次网格默认比主网格淡70% float mainGridAlpha = calculateGridAlpha(worldPos.xz, mainGridStep); float subGridAlpha = calculateGridAlpha(worldPos.xz, subGridStep) * 0.3; float finalAlpha = max(mainGridAlpha, subGridAlpha); // 远处网格渐隐,避免过密产生噪点闪烁 float distanceFade = 1.0 - clamp(length(worldPos.xz) / cameraFar, 0.0, 1.0); finalAlpha *= distanceFade * distanceFade; // 透明度过低直接丢弃减少性能消耗 if (finalAlpha < 0.01) discard; fragColor = vec4(gridColor, finalAlpha); }
低版本适配方案
如果你的运行环境不支持gl_FragDepth自定义深度写入,可直接删除片元着色器中深度写入相关的代码,调整渲染顺序为所有不透明物体渲染完成后再绘制网格,即可正常显示,仅会出现网格覆盖近处透明物体的小瑕疵,不影响基础使用。
内容的提问来源于stack exchange,提问作者KLD
相关产品推荐
相关产品推荐

