【Virtual Globe 渲染技术笔记】6 着色

着色(Shading)

曲面细分只是地球渲染的第一步。接下来是着色——通过模拟光线与材质的相互作用,计算每个像素的最终颜色。本节先回顾基础的光照与纹理映射,再讲解虚拟地球特有的经纬网格夜景灯光效果。


6.1 光照(Lighting)

我们从最简“直通”着色器(Listing 1)开始,逐步加入漫反射与镜面反射。

/*----------------List 1----------------------*/
// Vertex shader
in vec4 position;
uniform mat4 ce_modelViewPerspectiveMatrix;

void main()
{
   gl_Position = ce_modelViewPerspectiveMatrix * position ;
}

// Fragment shader
out vec3 fragmentColor;
void main() {
   fragmentColor = vec3(0.0, 0.0, 0.0);
}

  • 顶点着色器
    使用自动变量 ce_modelViewPerspective 把顶点坐标变换到裁剪空间。
    此时球体全黑。
    在这里插入图片描述

  • 光源位置
    采用“眼旁点光源”:光源放在相机位置,仿佛用户提着一盏灯。
    为简化,光照计算在世界坐标系完成(示例仅有一个坐标系)。

  • 逐像素光照
    在片元着色器计算 Phong 光照,可消除因插值导致的高光锯齿,也便于后续纹理特效。

漫反射
粗糙表面散射光线,强度仅与入射角有关:
Idiffuse=max⁡(n^⋅l^,0) I_{\text{diffuse}} = \max(\hat{\mathbf n}\cdot \hat{\mathbf l},0) Idiffuse=max(n^l^,0)
其中 n^\hat{\mathbf n}n^ 为法线,l^\hat{\mathbf l}l^ 为光源方向。
球面法线可直接归一化世界坐标得到;椭球需用 GeodeticSurfaceNormal

镜面反射
光滑表面产生高光,强度与视线方向有关:
Ispec=(r^⋅v^)α,r^=2(n^⋅l^)n^−l^ I_{\text{spec}} = \left(\hat{\mathbf r}\cdot \hat{\mathbf v}\right)^\alpha,\quad \hat{\mathbf r}=2(\hat{\mathbf n}\cdot\hat{\mathbf l})\hat{\mathbf n}-\hat{\mathbf l} Ispec=(r^v^)α,r^=2(n^l^)n^l^
指数 α\alphaα 决定高光锐利度。
最终光照:
I=kdIdiffuse+ksIspec+ka I = k_dI_{\text{diffuse}} + k_sI_{\text{spec}} + k_a I=kdIdiffuse+ksIspec+ka

实现见 Listing 2(顶点)与 Listing 3(片元)。

/*-----------Listing 2--------------------*/
// Vertex shader for diffuse and specular lighting.
in vec4 position;
out vec3 worldPosition;
out vec3 positionToLight;
out vec3 positionToEye;

uniform mat4 ce_modelViewPerspectiveMatrix;
uniform vec3 ce_cameraEye;
uniform vec3 ce_cameraLightPosition;

void main()
{
	gl_Position = ce_modelViewPerspectiveMatrix * position;
	worldPosition = position.xyz;
	positioinToLight = ce_cameraLightPosition - worldPosition;
	positionToEye = ce_cameraEye - worldPosition;
}

/*-----------Listing 3--------------------*/
// Final Phong-lighting fragment shader.
in vec3 worldPosition;
in vec3 positionToLight;
in vec3 positionToEye;
out vec3 fragmentColor;

uniform vec4 ce_diffuseSpecularAmbientShininess;

float LightIntensity(vec3 normal, vec3 toLight, vec3 toEye, vec4 diffuseSpecularAmbientShininess)
{
    vec3 toReflectedLight = reflect(-toLight, normal);
    float diffuse = max(dot(toLight, normal), 0.0);
    float specular = max(dot(toReflectedLight, toEye), 0.0);
    specular = pow(specular, diffuseSpecularAmbientShininess.w);
    
    return (diffuseSpecularAmbientShininess.x * diffuse) + 
           (diffuseSpecularAmbientShininess.y * specular) + 
           diffuseSpecularAmbientShininess.z;
}

void main()
{
    vec3 normal = normalize(worldPosition);
    float intensity = LightIntensity(normal, 
                                    normalize(positionToLight), 
                                    normalize(positionToEye), 
                                    ce_diffuseSpecularAmbientShininess);
    fragmentColor = vec3(intensity, intensity, intensity);
}


6.2 纹理映射(Texturing)

光照体现曲率,但地球真正魅力来自高分辨率影像。本节讲解逐像素计算纹理坐标(假设纹理一次性装入显存,且 float 精度足够)。
在这里插入图片描述

  • 世界影像通常 2:1 宽高比,WGS84 坐标。

  • 给定片元法线 n=(nx,ny,nz)∈[−1,1]\mathbf n=(n_x,n_y,n_z)\in[-1,1]n=(nx,ny,nz)[1,1],计算 (s,t)∈[0,1](s,t)\in[0,1](s,t)[0,1]
    s=atan2(ny,nx)2π+0.5,t=arcsin⁡nzπ+0.5. \begin{aligned} s &= \frac{\text{atan2}(n_y,n_x)}{2\pi}+0.5,\\[2pt] t &= \frac{\arcsin n_z}{\pi}+0.5. \end{aligned} st=2πatan2(ny,nx)+0.5,=πarcsinnz+0.5.
    该公式把经纬度映射到纹理空间(Listing 4.11)。

  • 光照×颜色:
    finalColor=texture(uday,(s,t))×I \text{finalColor} = \text{texture}(u_{\text{day}},(s,t)) \times I finalColor=texture(uday,(s,t))×I
    (图 4.11(c))

极点问题
纹理极区像素密度过高,过滤反而加剧失真。
EVE Online 采用“平面+球面”混合投影;也可改用立方体贴图避免极点拉伸,但在每个立方体面的边界处会引入轻微的畸变。


6.3 CPU / GPU 权衡

方案优点缺点
逐片元 计算法线/纹理坐标节省显存;无插值误差;代码简洁;无 IDL 特殊处理GPU 反三角函数精度/速度低
逐顶点 计算并存储顶点着色器简单;一次计算多次使用顶点数据翻倍;顶点插值导致 IDL 纹理跳变(下图)

在这里插入图片描述


6.4 经纬网格(Latitude-Longitude Grid)

几乎所有虚拟地球都可叠加经纬网(图 4.13)。常见做法:

  • CPU 生成折线;
  • 缓存网格,视角移动时复用;
  • 根据缩放级别动态增减分辨率(LOD)。

片元着色器方案
优点:

  • 无 CPU 计算;
  • 无额外显存/带宽;
  • 无 Z-fighting;
  • 无需额外 Pass。

缺点:

  • 单 Pass 渲染耗时稍长;
  • 受 32-bit float 精度限制;
  • 文字标注需额外处理。

实现要点

  • 通过纹理坐标 (s,t) 判断是否在网格线附近;
  • dFdx/dFdy 获得屏幕空间梯度,实现像素恒定线宽(Listing 4);
  • 支持不同颜色、线宽、淡入淡出、抗锯齿及线型。
/*------------------------Listing 4-------------------------------*/
void main()
{
    vec3 normal = GeodeticSurfaceNormal(worldPosition, u_globeOneOverRadiiSquared);
    vec2 textureCoordinate = ComputeTextureCoordinates(normal);
    vec2 distanceToLine = mod(textureCoordinate, u_gridResolution);
    
    vec2 dx = abs(dFdx(textureCoordinate));
    vec2 dy = abs(dFdy(textureCoordinate));
    vec2 dF = vec2(max(dx.s, dy.s), max(dx.t, dy.t)) * u_gridLineWidth;
    
    if (any(lessThan(distanceToLine, dF)))
    {
        fragmentColor = vec3(1.0, 0.0, 0.0);
    }
    else
    {
        float intensity = LightIntensity(normal,
                                        normalize(positionToLight),
                                        normalize(positionToEye),
                                        ce_diffuseSpecularAmbientShininess);
        fragmentColor = intensity * texture(ce_texture0, textureCoordinate).rgb;
    }
}

LOD 控制
根据相机高度分段设置 u_gridResolution

  • 定义高度区间 → 网格分辨率映射表;
  • 利用时间连续性,优先检查上一区间,查找几乎 O(1)。
    在这里插入图片描述

6.5 夜景灯光(Night Lights)

虚拟地球常在背阳面显示城市灯光——经典多重纹理应用:
白天纹理 + 夜间灯光纹理,按太阳照射角度混合。

片元着色器流程

  • 顶点着色器传入太阳位置 og_sunPosition
  • 新增 uniform:
    • u_dayTexture, u_nightTexture
    • u_blendDuration 过渡时长
    • u_blendDurationScale = 1/(2u_blendDuration)(预计算)
  • 片元着色器:
    1. 计算漫反射因子 d=max⁡(n^⋅l^,0)d = \max(\hat{\mathbf n}\cdot\hat{\mathbf l},0)d=max(n^l^,0)
    2. d>ublendDurationd > u_{\text{blendDuration}}d>ublendDuration:用白天纹理 + Phong 光照。
    3. d<−ublendDurationd < -u_{\text{blendDuration}}d<ublendDuration:用夜间纹理,无光照。
    4. 介于两者之间:线性混合昼夜颜色。
      在这里插入图片描述

性能实验

  • 仅看昼面 vs 仅看夜面:夜面帧率更高(夜间纹理分辨率低且无光照计算)。
  • 游戏常用技巧:用纹理图集 + 旋转/镜像,少量纹理即可产生丰富夜景变化(EVE Online)。

多重纹理其他应用

  • 云层纹理
  • 水面高光贴图(gloss map)
    早期硬件不支持多重纹理时,STK 采用多 Pass 实现夜景。

参考:

  • Cozi, Patrick; Ring, Kevin. 3D Engine Design for Virtual Globes. CRC Press, 2011.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值