You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 16:36:03