CubeMap实现天空盒

在我的另一篇文章已经写了关于天空盒实现的原理,其中提到了半球天空盒的原理其中说到的算法就是这篇文章要讲到的东西,关于这个算法,微软其实已经帮我们实现了,我们只要会使用就可以了。CubeMap本质上来说不是一张纹理,而是六张可以无缝拼接的纹理,而CubeMapping则是多组组纹理构成每一组由六个纹理构成,不同等级的组织间的画质也不也一样,mip等级越高画质越好(通常来说每个小组的纹理都是同样的内容,只是画质不一样,但如果有意去把不同层级之间的纹理内容设置不同也是可以滴)。

天空盒

天空盒

CubeMpa的映射

CubeMap的映射与普通纹理的映射不一样,CubeMap是我们很少用到的三维纹理中比较常用到的,通常都是用来做天空盒,CubeMap的映射不采用UV坐标而采用三维坐标X轴Y轴Z轴,一个CubeMap六张图组成,索引从0到5依次对应正X面,负X面,正Y面,负Y面,正Z面,负Z面。CubeMap的像素映射与一个起点在原点的查找向量V有关,向量V可以设想为以立方体的中心为起点,指向立方体外面。该向量与立方体的交点处对应的像素就是我们映射到模型上的像素,像素的说法通常是说平面图片的,在书上经常会看到纹素(texel)的说法,只是博主觉得像素更贴切而已(随你们怎么叫)。下图就是CubeMap映射的示意图。

CubeMap映射

CubeMap映射

VertexOut VS(VertexIn vin)
{
    VertexOut vout;
    vout.PosL = vin.PosL;

    float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);

    //让球形/半球天空盒一直跟随天空盒,啥摄像机就出不了天空盒的范围
    posW.xyz += gEyePosW;

    vout.PosH = mul(posW, gViewProj).xyww;

    return vout;

}

float4 PS(VertexOut pin) : SV_Target
{
    //对CubeMap进行采样 gsamLinearWrap这是线性重复采样器,随便种采样器都可以(采样器本身的采样方式没效果)
    return gCubeMap.Sample(gsamLinearWrap, pin.PosL);
}

模拟反射

根据这种天空盒的算法,又可以演变出一种模拟反射的算法,将天空盒上的像素反射到摄像机中,类似于水中的倒影,往水面看去可以看到天空中的云朵,如下图所示图中的V是指向摄像机的向量,起点为P点,根据P处的法线N(法向量)可以算出V关于N的反射向量R,然后使用R对CubeMap天空盒进行采样,采样的结果结合菲涅尔效应就可以模拟出反射的效果了。

模拟反射

模拟反射

	float3 r = reflect(-toEyeW, pin.NormalW);
	float4 reflectionColor = gCubeMap.Sample(gsamLinearWrap, r);
	float3 fresnelFactor = SchlickFresnel(fresnelR0, pin.NormalW, r);
	//计算出的结果叠乘上光滑度和菲涅尔因子再叠加给光照
	litColor.rgb += shininess * fresnelFactor * reflectionColor.rgb;

这种反射其实有点缺陷,因为向量只有方向没有位置,所以不管在一个游戏场景中的哪一点,摄像机以相同的角度观察一个平滑的平面,看到的反射都是一样的,观察角度一样法线方向一样,观察出来的效果也一样,尤其是平滑平面的时候,所有的反射向量R的起点都默认在了CubeMap的中心点,如果想要实现更好的效果就需要使用到其他技术了,比如像包围盒技术,将包围盒输入到像素着色器中进行相交检测。

未经允许不得转载:他日重逢 » CubeMap实现天空盒

赞 (2) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

隐藏
变装