找回密码
 立即注册
查看: 395|回复: 3

[unity] 实现大地形渲染之 geometry clipmap

[复制链接]
发表于 2022-3-16 09:25 | 显示全部楼层 |阅读模式
--------------------------------------------------更新中---------------------------------------------------
最近希望在unity中实现一个简单的geometry clipmap,用于飞行模拟游戏中巨大地形的渲染。但是网上冲浪之后发现资料有点少,只好一点点从头做起。那么就用本文记录一些个人比较迷惑的地方,作为我的备忘录和经验分享吧。
参考资料:GPU Gems 2, geometry clipmap 原始论文
算法整体框架




Per-frame algorithm

从论文中可以清晰得知,算法需要三个步骤:计算active region(我们希望渲染的地形范围);更新geometry clipmap(多级地形纹理/贴图);裁剪渲染范围至贴图范围,并进行渲染。
代码解读

Upsample(上采样)

算法通过对较粗糙的高度图纹理进行插值来获取分辨率更高(一级)的高度图。
不过在gpu gems提供的示例代码中,为了实现不同类型坐标的插值计算,这个算法写的比较绕,它的pixel shader是这样的:
float4 UpsamplePS(float2 p_uv : TEXCOORD0) : COLOR
{
    float residual = tex2D(ResidualSampler, p_uv*OneOverSize);  
   
    p_uv = floor(p_uv);
    float2 p_uv_div2 = p_uv/2;
    float2 lookup_tij = p_uv_div2+1;
    float4 maskType = tex2D(LookupSampler, lookup_tij);     
         
    matrix maskMatrix[4];
    maskMatrix[0] = matrix(0, 0, 0, 0,
                           0, -1.0f/16.0f, 0, 0,
                           0, 0, 0, 0,
                           1.0f/256.0f, -9.0f/256.0f, -9.0f/256.0f, 1.0f/256.0f);
                           
    maskMatrix[1] = matrix(0, 1, 0, 0,
                           0, 9.0f/16.0f, 0, 0,
                           -1.0f/16.0f, 9.0f/16.0f, 9.0f/16.0f, -1.0f/16.0f,
                           -9.0f/256.0f, 81.0f/256.0f, 81.0f/256.0f, -9.0f/256.0f);                        
                           
    maskMatrix[2] = matrix(0, 0, 0, 0,
                           0, 9.0f/16.0f, 0, 0,
                           0, 0, 0, 0,
                           -9.0f/256.0f, 81.0f/256.0f, 81.0f/256.0f, -9.0f/256.0f);
                           
    maskMatrix[3] = matrix(0, 0, 0, 0,
                           0, -1.0f/16.0f, 0, 0,
                           0, 0, 0, 0,
                           1.0f/256.0f, -9.0f/256.0f, -9.0f/256.0f, 1.0f/256.0f);

    float2 offset = float2(dot(maskType.bgra, float4(1, 1.5, 1, 1.5)), dot(maskType.bgra, float4(1, 1, 1.5, 1.5)));
   
    float z_predicted=0;
    offset = (p_uv_div2-offset+0.5)*OneOverSize+TextureOffset;
    for(int i = 0; i < 4; i++) {
        float zrowv[4];
        for (int j = 0; j < 4; j++) {
                float2 vij    = offset+float2(i,j)*OneOverSize;
                zrowv[j]      = tex2D(CoarseLevelElevationSampler, vij);
        }
        
        vector mask = mul(maskType.bgra, maskMatrix);
        vector zrow = vector(zrowv[0], zrowv[1], zrowv[2], zrowv[3]);
        zrow = floor(zrow);
        z_predicted = z_predicted+dot(zrow, mask);
    }

   
    z_predicted = floor(z_predicted);
   
    // add the residual to get the actual elevation
    float zf = z_predicted + residual;  
   
    // zf should always be an integer, since it gets packed
    //  into the integer component of the floating-point texture
    zf = floor(zf);
   
    float4 uvc = floor(float4((p_uv_div2+float2(0.5f,0)),
                              (p_uv_div2+float2(0,0.5f))))*OneOverSize+TextureOffset.xyxy;
            
    // look up the z_predicted value in the coarser levels  
    float zc0 = floor(tex2D(CoarseLevelElevationSampler, float4(uvc.xy, 0, 1)));
    float zc1 = floor(tex2D(CoarseLevelElevationSampler, float4(uvc.zw, 0, 1)));        
   
    float zf_zd = zf + ((zc0+zc1)/2-zf+256)/512;

    return float4(zf_zd, 0, 0, 0);
}这个LookupSampler(texture)和maskMatrix比较魔法,在此分析一下。
我们在上采样时要从粗糙lv0的高度图(方框)插值计算出更精确一级lv1的高度图(圆圈),它们之间的关系如图所示。为了方便起见,假设我们的uv坐标是一个单位对应一像素(注意在lv0中坐标细分为0.5),那么lv1在lv0中的坐标有4种类型:

  • 和上一级重合:(0,0)
  • 在两个像素中央:(0,0.5),(0.5,0)
  • 在四个像素中央:(0.5,0.5)



两个等级的高度图

那么通过权重为(-1/16, 9/16, 9/16, -1/16)的四点法插值



A 4-point interpolatory subdivision scheme for curve design

这四种类型的坐标的计算方式分别需要1、4、4、16个点的数据,如下图不同颜色的方框所示。



采样数据范围

比较直观的插值公式如右侧的矩阵所示,直接对最大范围的16点数据与权重(方便起见省去了分母 /256)进行点乘即可。但是因为gpu不便处理分支语句,gpu gems里使用循环+额外的一张2*2控制纹理来实现算法,这些权重矩阵则被拆散进了maskMatrix中(如左侧所示)。比如右侧矩阵1的第一行在左侧矩阵4的第一行(这里为了和图片对应,行坐标颠倒了),而其第二行则在左侧矩阵3的第一行,第三行在左侧矩阵2的第一行......依次类推。控制纹理中则简单地记录了4个one-hot向量:

  • (1,0,0,0):对应坐标类型(0,0)
  • (0,1,0,0):对应坐标类型(0,0.5)
  • (0,0,1,0):对应坐标类型(0.5, 0)
  • (0,0,0,1):对应坐标类型(0.5,0.5)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-3-16 09:26 | 显示全部楼层
好奇为啥这么搞不会在每一级边缘处出现明显裂缝…无缝的搞法不是用细分那套算法吗
发表于 2022-3-16 09:32 | 显示全部楼层
会有t junction的,不过高度图有过渡区
发表于 2022-3-16 09:36 | 显示全部楼层
这个用瞄准镜的时候怎么处理,不处理会有空气墙,处理了又怕性能爆炸
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 16:44 , Processed in 0.108690 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表