第十章 高级纹理

发布于:2024-04-18 ⋅ 阅读:(23) ⋅ 点赞:(0)

立方体纹理

立方体纹理是环境映射的一种实现方法。环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了一层金属一样反射出周围的环境。

立方体纹理一共包含6张图形,这些图像对应了立方体六个面。对立方体纹理采样我们需要提供一个三维的纹理坐标,这个纹理坐标表示了我们在世界空间下的一个3D方向。这个方向矢量从立方体的中心出发,当它向外部延伸时就会和立方体的6个纹理之一发生相交,采样得到的结果就是该点计算来的。

好处在于实现简单快速,效果好。缺点是当场景中引入新的物体、光源或者发生移动时,就需要重新生成立方体纹理。立方体纹理也仅可以反射环境,但不能反射使用了该立方体纹理的物体本身。

立方体纹理不能模拟多次反射的结果。

天空盒子

当我们在场景中使用天空盒子时,整个场景就被包围在一个立方体内。这个立方体的每个面使用的技术就是立方体纹理映射技术。

在Unity中,天空盒子是在所有不透明物体之后渲染的,而其背后使用的网格是一个立方体或一个细分后的球体。

创建用于环境映射的立方体纹理

立方体最常见的用处是用于环境映射,模拟金属质感的材质。

反射

使用反射的物体通常看起来像镀了层金属

通过入射光线的方向和表面法线方向来计算反射方向,再利用反射方向对立方体纹理采样即可。

Shader "Unity Shaders Book/Chapter 10/Reflection"
{
    Properties
    {
        _Color          ("Color Tint", Color) = (1, 1, 1, 1)
        //控制反射颜色
        _ReflectColor   ("Reflection Color", Color) = (1, 1, 1, 1)
        //控制材质反射程度
        _ReflectAmount  ("Reflect Amount", Range(0, 1)) = 1
        //用于模拟反射的环境映射纹理
        _Cubemap        ("Reflection Cubemap", Cube) = "_Skybox"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}

        Pass
        {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM

            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
			#include "AutoLight.cginc"

            fixed4 _Color;
			fixed4 _ReflectColor;
			fixed _ReflectAmount;
			samplerCUBE _Cubemap;

            struct  a2v{
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f{
                float4 pos : SV_POSITION;
				float3 worldPos : TEXCOORD0;
				fixed3 worldNormal : TEXCOORD1;
				fixed3 worldViewDir : TEXCOORD2;
				fixed3 worldRefl : TEXCOORD3;
				SHADOW_COORDS(4)
            };

			//计算了该顶点的反射方向
            v2f vert(a2v v) {
				v2f o;
				
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
				
				//物体反射到摄像机中的光线方向,可以由光路可逆的原则反向求得
				//计算视角方向关于顶点法线的反射方向来求得入射光线的方向
				o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
				
				TRANSFER_SHADOW(o);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));		
				fixed3 worldViewDir = normalize(i.worldViewDir);		
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
				
				// 没有对i.worldRefl做归一化操作,因为用于采样的参数仅仅是作为方向变量传递给texCUBE函数的
				fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
				
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				
				//使用_ReflectAmount来混合漫反射颜色和