[OpenGL] 动态积雪效果
发布日期:2021-06-29 07:15:32 浏览次数:5 分类:技术文章

本文共 3066 字,大约阅读时间需要 10 分钟。

默认
动态生成积雪

 

        对于游戏中模拟雪天而言,除了天上飘落的雪花外,我们通常还希望能够在一个现成的场景地图上,生成场景物体被积雪覆盖的效果。让美术为每个场景物件单独做雪效果的材质可以得到比较细腻的效果,但这会成倍地增加美术制作成本。一个比较的常见的思路是,直接将雪的贴图从上往下“叠加”到场景物体上,通过这样的方式,就能快速地让场景中的物体都被雪覆盖。

        

实现思路

        我们认为雪是从天上落下的,不考虑风向的话,可利用物体表面的法线和向上向量(vector(0,1,0))的余弦值,如果大于0,则表面被雪覆盖,且余弦值越大,积雪程度越大。根据积雪程度,我们混合原纹理和雪纹理。

       由于直接使用余弦值作为积雪程度会使得整体雪过于稀疏,我们可以对其做一个简单的映射(如果想要得到雪更密的效果,可以调小param的值或者使用别的映射函数)

float param = 0.3;float snowRatio = max(dot(normal,vec3(0,1,0)),0);snowRatio = snowRatio/(snowRatio + param);

       以下是使用映射min(snowRatio * 1.2 /(snowRatio + 0.3),1),不添加法线贴图得到的球体积雪效果:

       

       得到了积雪程度后,我们开始考虑从雪纹理中采样,因此我们需要确认采样纹理的uv坐标。根据一开始的设想,我们是从(y轴方向)上到下向整个场景投影了一张雪的纹理,也就相当于,我们可以直接用物体的世界坐标x和z分量作为uv坐标。该过程适用于前向渲染和延迟渲染,但对于前者,相当于要在每个场景对象的着色器最后加入积雪采样的部分,着色器很多时编写和维护比较麻烦。因此,此处是在延迟渲染的屏幕空间完成这一操作的。

        此处根据自己的需要对纹理坐标进行缩放,采样雪的颜色和法线纹理:

float scale = 40;vec3 snowColor = vec3(texture2D(Snow_D, vec2(worldPos.x,worldPos.z)/scale));vec3 snowNormal = UnpackNormal(vec3(texture2D(Snow_N, vec2(worldPos.x,worldPos.z)/scale)));

        特别要注意的是法线的解码部分,在延迟渲染中,我们通常只有解码后的物体法线信息,而没有存储TBN矩阵中的N、T等信息,而由于雪法线纹理的特殊性(我们可以粗略认为它的N就是沿着y轴向上的),我们还是可以构造出TBN矩阵:

vec3 UnpackNormal(vec3 n){    vec3 N = vec3(0,1,0);    vec3 T = vec3(1,0,0);    vec3 B = cross(N, T);//vec3(0,0,1);    mat3 TBN = mat3(T,B,N);    n = normalize(2 * n - 1);    n = normalize(TBN * n);    return n;}

       之后根据比例对原颜色和积雪颜色,原法线和积雪法线进行混合(此处混合的法线是世界空间的法线,不确定是否能够这么直接做线性插值,但看起来表现没什么问题):

albedo = mix(albedo,snowColor,snowRatio);normal = mix(normal,snowNormal,snowRatio);

      现在我们已经基本得到积雪的效果了。但如果我们的场景再丰富一些的话,比如有一些亭子之类的物件,我们会发现漏雪现象——被屋顶遮挡的地面也出现了大片的积雪。

      此处的修改想法是,先预处理一张从上往下视角观察的深度图(正交投影即可),然后在计算像素的积雪填充颜色时,比较物体深度和采样得到的深度大小,如果比较相近,说明是积雪覆盖的区域,否则被遮挡。

      正交投影的上下左右参数可依据场景在x,z上的范围来决定,远近裁剪面由场景的高度范围决定。

      此外,还有一种思路是,动态的根据视锥体计算正交投影的范围,获取其AABB包围盒(直接求得视锥体的8个点,然后取每个分量的最小值和最大值)

       

      我们需要构造两个矩阵。一个是积雪方向观察的视图矩阵,lookAt方向沿y轴向下;另一个是我们刚才提到的正交投影矩阵。

      之后,我们根据这两个矩阵写入积雪的深度:

#version 450 coreuniform mat4 OrthoMatrix;uniform mat4 SnowMatrix;uniform mat4 ModelMatrix;attribute vec4 a_position;varying vec2 v_depth;void main(){    gl_Position = ModelMatrix * a_position;    gl_Position = SnowMatrix * gl_Position;    gl_Position = OrthoMatrix * gl_Position;    v_depth = gl_Position.zw;}
#version 450 corevarying vec2 v_depth;void main(){    float fColor =  (v_depth.x/v_depth.y + 1)/2;    gl_FragColor.x = fColor;}

        然后在屏幕空间做深度比较,对得到的结果生成一个积雪比例,并与之前的积雪比例相乘,得到最终的积雪比例。此处从相邻的深度采样做混合,得到比较平滑的积雪边缘过渡效果:

float GetSnowCoverRatio(vec4 worldPos){    vec4 snowPos = OrthoMatrix * (SnowMatrix * worldPos);    float fDistance = (snowPos.z + 1)/2;    float fSnow = 0;    vec2 uv = snowPos.xy / snowPos.w * 0.5 + vec2(0.5, 0.5);    uv.x = clamp(uv.x, 0, 1);    uv.y = clamp(uv.y, 0, 1);    for(int i = -1; i <= 1; i++)    {        for(int j = -1; j <= 1; j++)        {            float fDistanceMap = texture2D(SnowDepth, uv + vec2(5.0 * i / ScreenX, 5.0 * j / ScreenY)).x;            fSnow += fDistance - 0.001 > fDistanceMap ? 0 : 1.0;        }    }    fSnow /= 9;    return fSnow;}
if(bSnow){    float coverRatio = GetSnowCoverRatio(vec4(worldPos,1));    float snowRatio = max(dot(normal,vec3(0,1,0)),0);    // ...    snowRatio *= coverRatio;    // ...}

 

转载地址:https://blog.csdn.net/ZJU_fish1996/article/details/89184477 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:[图形学] 实时体积云(Horizon: Zero Dawn)
下一篇:[图形学] 实时体积水和泡沫的渲染

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月13日 07时49分10秒