Unity Shader编程】之基础纹理

一,单张纹理

好的,用户想学习Unity Shader中的单张纹理章节。我需要根据提供的搜索结果来整理相关内容。首先,查看搜索结果中的相关部分,特别是‌、‌、‌、‌、‌这几条,因为它们提到了基础纹理、单张纹理的实现方法和相关属性设置。

用户问题中的“单张纹理章节”可能涉及纹理映射的基本概念、UV坐标、纹理属性设置以及在Shader中的具体应用。需要分步骤讲解如何实现单张纹理的渲染,包括Properties块的定义、顶点着色器和片元着色器的处理,以及纹理采样和光照计算。

要注意引用来源的标注,每个要点后面要加上对应的角标,比如‌提到纹理类型和UV坐标,‌和‌讨论了纹理属性和Wrap Mode,‌中的代码示例展示了如何声明变量和使用TRANSFORM_TEX。需要确保每个步骤都有对应的引用,并且结合多个来源的信息,避免重复引用同一个网页。

同时,用户之前的问题涉及法线贴图,但现在转向单张纹理,可能希望了解基础部分,所以需要从最基础的纹理映射开始,逐步展开。结构上可以分为基本概念、实现步骤、关键代码示例、属性设置和常见问题,每个部分引用相关搜索结果。确保回答清晰、结构化,并且每个点都有对应的来源支持。

单张纹理章节核心知识点总结‌

  1. 纹理映射基础‌
    定义‌:通过纹理映射(Texture Mapping)将2D图片逐纹素(Texel)映射到模型表面,控制模型颜色和细节‌。
    UV坐标‌:顶点存储的纹理映射坐标(二维变量 (u,v)),通常归一化到 [0,1] 范围,用于采样纹理‌。
    坐标系差异‌:
    OpenGL纹理空间原点在左下角,DirectX在左上角,Unity默认采用OpenGL风格‌。
  2. 单张纹理实现步骤‌
    2.1 声明纹理属性‌

在Shader的 Properties 块中定义纹理贴图及参数:

Properties {
_MainTex (“基础贴图”, 2D) = “white” {} // white 表示默认纯白色纹理‌:ml-citation{ref=“1,8” data=“citationList”}
_Color (“颜色调节”, Color) = (1,1,1,1) // 可选颜色混合参数‌:ml-citation{ref=“8” data=“citationList”}
}

需在CG代码中声明变量:

sampler2D _MainTex;
float4 _MainTex_ST; // 存储Tiling和Offset参数(xy为缩放,zw为偏移)‌:ml-citation{ref=“4,8” data=“citationList”}

2.2 顶点着色器处理UV‌

通过 TRANSFORM_TEX 宏应用Tiling/Offset:

struct v2f {
float2 uv : TEXCOORD0;
// 其他数据(如顶点位置、法线等)
};

v2f vert(appdata_base v) {
v2f o;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); // 应用缩放和偏移‌:ml-citation{ref=“1,8” data=“citationList”}
return o;
}

2.3 片元着色器采样与混合‌

采样纹理并与颜色参数混合,计算最终颜色:

fixed4 frag(v2f i) : SV_Target {
fixed4 texColor = tex2D(_MainTex, i.uv); // 采样纹理‌:ml-citation{ref=“1,6” data=“citationList”}
fixed3 albedo = texColor.rgb * _Color.rgb; // 混合颜色参数‌:ml-citation{ref=“8” data=“citationList”}
// 应用光照计算(如Lambert漫反射)
return fixed4(albedo, texColor.a);
}

  1. 纹理属性设置关键点‌
    Texture Type‌:
    单张纹理通常设为 Default,法线贴图需设为 Normal Map‌。
    Wrap Mode‌:
    Repeat:平铺纹理,适合无缝贴图‌;
    Clamp:边缘拉伸,避免接缝处采样异常‌。
    Filter Mode‌:
    Point:像素风格,性能最优‌;
    Bilinear/Trilinear:平滑过渡,质量更高但消耗更大‌。
  2. 常见问题与调试‌
    UV坐标异常‌:检查模型是否导出正确UV,或调整 _MainTex_ST 参数‌。
    颜色偏差‌:确保纹理导入设置中 sRGB 选项正确(颜色贴图启用,法线贴图禁用)‌。
    性能优化‌:
    移动端优先使用低分辨率纹理和 Bilinear 滤波‌;
    复杂场景启用Mipmap减少远处纹理锯齿‌。
  3. 完整代码示例(Surface Shader)‌
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/SingleTextureExample" {  
    Properties {  
        _MainTex ("Base Texture", 2D) = "white" {}  
        _Color ("Color Tint", Color) = (1,1,1,1)  
    }  
    SubShader {  
        Pass {  
            Tags { "LightMode" = "ForwardBase" }  
            CGPROGRAM  
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  

            struct appdata {  
                float4 vertex : POSITION;  
                float3 normal : NORMAL;  
                float2 uv : TEXCOORD0;  
            };  

            struct v2f {  
                float4 pos : SV_POSITION;  
                float2 uv : TEXCOORD0;  
                float3 worldNormal : TEXCOORD1;  
            };  

            sampler2D _MainTex;  
            float4 _MainTex_ST;  
            fixed4 _Color;  
            fixed4 _LightColor0;  

            v2f vert (appdata v) {  
                v2f o;  

                // --- 方法1:Unity内置函数(注释掉以禁用)---  
                // o.pos = UnityObjectToClipPos(v.vertex);  
                // o.uv = TRANSFORM_TEX(v.uv, _MainTex);  
                // o.worldNormal = UnityObjectToWorldNormal(v.normal);  

                // --- 方法2:手动计算(默认启用)---  
                o.pos = UnityObjectToClipPos(v.vertex);          // 手动MVP转换‌:ml-citation{ref="3,4" data="citationList"}  
                o.uv = v.uv * _MainTex_ST.xy + _MainTex_ST.zw;    // 手动计算Tiling/Offset‌:ml-citation{ref="2,6" data="citationList"}  
                o.worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal);  // 手动法线转换‌:ml-citation{ref="4,5" data="citationList"}  

                return o;  
            }  

            fixed4 frag (v2f i) : SV_Target {  
                // 纹理采样与颜色混合  
                fixed4 texColor = tex2D(_MainTex, i.uv);          // 采样主纹理‌:ml-citation{ref="1,2" data="citationList"}  
                fixed3 albedo = texColor.rgb * _Color.rgb;  

                // 光照计算(漫反射)  
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  
                float3 normal = normalize(i.worldNormal);  
                float diff = saturate(dot(normal, lightDir)*0.5+0.5);     // 漫反射强度‌:ml-citation{ref="4,5" data="citationList"}  
                fixed3 finalColor = albedo * _LightColor0.rgb * diff;  

                return fixed4(finalColor, texColor.a);  
            }  
            ENDCG  
        }  
    }  
    FallBack "Diffuse"  
}  

二,法线纹理

‌Unity 法线贴图设置与工作原理

‌法线贴图对顶点的影响机制‌
法线贴图‌不会直接修改顶点坐标‌,而是通过‌扰动表面法线方向‌改变光照计算结果,从而在视觉上模拟凹凸效果。以下是具体实现原理与步骤:
‌一、顶点与法线的关系‌
‌顶点数据基础‌:
顶点包含‌位置(Position)‌、‌法线(Normal)‌和‌切线(Tangent)‌等属性,定义了模型的几何形状与表面朝向‌。
顶点着色器中,通过模型变换矩阵(如 UNITY_MATRIX_MVP)将顶点从模型空间变换到裁剪空间‌。
‌法线贴图的作用‌:
法线贴图存储的是‌切线空间下的法线偏移‌(RGB 通道映射到 [-1,1] 范围),而非顶点位置信息‌。
通过修改法线方向,改变‌光线反射方向‌,在固定顶点位置下模拟表面凹凸感‌。

将贴图设为 ‌Normal Map‌ 类型后,Unity 通过以下流程将其识别为物体法线方向,并参与光照计算:

‌**1. ‌贴图类型标记的作用
数据格式处理‌:标记为 Normal Map 后,Unity 会针对不同平台(如 PC、移动端)自动采用合适的压缩格式(如 DXT5nm)‌。
法线解压优化‌:通过 UnpackNormal 函数将法线贴图的颜色值(01)映射回三维法线向量(-11),例如:
glsl
Copy Code
float3 normalTangent = UnpackNormal(tex2D(_NormalMap, uv)); // 解压法线‌:ml-citation{ref=“1,3” data=“citationList”}

此步骤确保法线方向的正确性‌。
‌**2. ‌法线贴图的转换流程
切线空间→世界空间‌:
顶点着色器‌传递模型的‌切线‌(Tangent)、‌副切线‌(Binormal)和‌原始法线‌(Normal),构建 ‌TBN 矩阵‌(Tangent-Binormal-Normal Matrix)‌。

float3x3 TBN = float3x3(  
    normalize(worldTangent),  
    normalize(worldBinormal),  
    normalize(worldNormal)  
); // TBN矩阵构建‌:ml-citation{ref="4,5" data="citationList"}  

片元着色器‌通过 TBN 矩阵将切线空间法线转换到世界空间:

float3 worldNormal = mul(normalTangent, TBN); // 切线→世界空间转换‌:ml-citation{ref="4,5" data="citationList"}  

‌**3. ‌法线方向的应用
光照计算‌:转换后的世界空间法线参与光照模型(如 Blinn-Phong 或 PBR),直接影响漫反射和高光效果。例如:

float diff = saturate(dot(worldNormal, lightDir)); // 漫反射计算‌:ml-citation{ref="4,6" data="citationList"}  
float3 halfDir = normalize(lightDir + viewDir);  
float spec = pow(saturate(dot(worldNormal, halfDir)), _Gloss * 128); // 高光计算‌:ml-citation{ref="4,6" data="citationList"}  

关键注意事项‌

*贴图导入设置‌: 必须将法线贴图类型设为 Normal Map,否则 UnpackNormal 无法正确解压数据‌。 Unity 默认法线贴图的 ‌z 分量为正‌,仅存储 x、y 分量(z 通过计算得出)‌。

Shader 属性命名‌: 法线贴图变量需命名为 _BumpMap 或 _NormalMap,否则 Unity 内置管线可能无法识别‌。 非均匀缩放修正‌: 若模型存在缩放,需通过‌逆转置矩阵‌修正法线方向:*

**float3 worldNormal = mul(transpose(inverse((float3x3)unity_ObjectToWorld)), normal);  

避免因缩放导致法线方向错误‌。
效果对比‌
未用法线贴图‌ ‌使用法线贴图‌
表面平滑无细节 凹凸细节清晰,光影动态变化
依赖模型顶点法线 低模即可实现高精度效果**

通过上述流程,Unity Shader 能够将法线贴图的颜色数据解析为法线向量,并动态影响光照计算,最终实现高细节的表面效果‌。

法线纹理的两种计算方式对比‌

  1. 切线空间计算(Tangent Space)‌

核心逻辑‌:在切线空间下直接处理光照方向、视角方向,将光照方向转换到切线空间进行计算‌。
优势‌:

效率高‌:矩阵变换(如光照方向转换)可在顶点着色器完成,减少片元着色器计算量‌。
灵活性高‌:
支持 ‌UV 动画‌(如水流、熔岩动态效果)‌;
法线贴图可跨模型复用(记录相对法线信息,不受模型变形影响)‌。
可压缩‌:仅需存储法线贴图的 XY 分量(Z 方向由归一化推导得出)‌。

劣势‌:

全局效果精度低‌:
处理镜面反射、环境映射等全局效果时可能不够准确‌;
切线空间法线需逐顶点构造 TBN 矩阵,模型变形(如骨骼动画)可能导致误差‌。
2. 世界空间计算(World Space)‌

核心逻辑‌:将法线贴图从切线空间转换到世界空间,直接在世界坐标系下进行光照计算‌。
优势‌:

全局效果精准‌:
光照、反射等计算基于统一的世界坐标系,精度更高‌;
适用于复杂光照场景(如动态阴影、全局光照)‌。
实现直观‌:无需处理切线空间与模型空间的关系,逻辑更清晰‌。

劣势‌:

性能开销大‌:
TBN 矩阵构造和法线空间转换需在片元着色器执行,计算密集‌;
对低端设备不友好(如移动端)‌。
兼容性差‌:
法线贴图无法跨模型复用(存储绝对法线方向)‌;
模型变形时需重新计算 TBN 矩阵‌。
适用场景总结‌
计算方式‌ ‌推荐场景‌ ‌不适用场景‌
切线空间计算‌ 移动端/性能敏感场景、UV 动画、模型复用需求‌ 高精度全局光照、动态模型变形‌
世界空间计算‌ PC/主机端、复杂光照场景(如镜面反射、环境映射)‌ 低性能设备、需 UV 动画的场景‌

关键结论‌

效率优先选切线空间‌:适合性能敏感场景及动态效果需求‌;
效果优先选世界空间‌:需高精度全局光照时不可替代‌。

1,‌切线空间法线贴图实现原理与步骤

基本思路:再片元着色器中通过纹理采样得到切线空间下的法线,然后再与切线空间下的视角方向,光照方向等进行计算。为此,我们需要再顶点着色器中把视角方向和光照方向从模型空间变到切线空间中
切线空间法线贴图通过‌顶点切线坐标系‌存储法线偏移,在 Shader 中通过 ‌TBN 矩阵‌将法线转换到世界空间参与光照计算。以下是完整实现流程:
‌一、原理概述‌
‌切线空间特性‌:
切线空间由顶点‌切线(T)‌、‌副切线(B)‌、‌法线(N)‌构成,法线贴图存储的是该空间下的法线偏移‌。
在此空间下,法线方向‌无需依赖模型姿态‌,仅需将光照方向、视角方向等外部向量转换到切线空间,即可直接参与计算‌13。
‌核心步骤‌:
‌顶点着色器‌:将模型空间的光照方向、视角方向转换到切线空间。
‌片元着色器‌:采样法线贴图,与切线空间的光照方向、视角方向计算光照‌。

Shader "Custom/TangentSpaceNormal" {
    Properties {
        _MainTex ("Albedo", 2D) = "white" {}
        _BumpMap ("Normal Map", 2D) = "bump" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT; // tangent.w用于副切线方向修正
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                // 切线空间下的光照方向与视角方向
                float3 lightDirTS : TEXCOORD1; // Tangent Space Light Direction
                float3 viewDirTS : TEXCOORD2;  // Tangent Space View Direction
            };

            sampler2D _MainTex, _BumpMap;

            v2f vert(appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;

                // 计算TBN矩阵
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
                float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
                float3x3 TBN_World = float3x3(worldTangent, worldBinormal, worldNormal);

                // 模型空间→切线空间转换矩阵
                float3x3 TBN_Object = mul(TBN_World, (float3x3)unity_WorldToObject);

                // 将光照方向、视角方向转换到切线空间
                float3 lightDirObj = normalize(ObjSpaceLightDir(v.vertex)); // 模型空间光照方向
                float3 viewDirObj = normalize(ObjSpaceViewDir(v.vertex));    // 模型空间视角方向
                o.lightDirTS = mul(TBN_Object, lightDirObj);                 // 模型→切线空间
                o.viewDirTS = mul(TBN_Object, viewDirObj);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                // 采样法线贴图(切线空间)
                fixed3 normalTS = UnpackNormal(tex2D(_BumpMap, i.uv));

                // 切线空间下的光照计算(以Blinn-Phong为例)
                float3 lightDirTS = normalize(i.lightDirTS);
                float3 viewDirTS = normalize(i.viewDirTS);
                float3 halfDirTS = normalize(lightDirTS + viewDirTS);

                // 漫反射
                float diff = saturate(dot(normalTS, lightDirTS));
                // 高光
                float spec = pow(saturate(dot(normalTS, halfDirTS)), 32);

                fixed4 albedo = tex2D(_MainTex, i.uv);
                return albedo * _LightColor0 * (diff + spec);
            }
            ENDCG
        }
    }
}

在这里插入图片描述

2,‌世界空间法线贴图实现原理与步骤

‌顶点着色器阶段‌:

构建‌TBN矩阵的逆矩阵‌(Model→Tangent空间)‌。
将模型空间的法线、切线、副切线转换到‌世界空间‌,构建‌World→Tangent矩阵‌‌45。
‌片元着色器阶段‌:

采样法线贴图后,需通过矩阵将法线从‌切线空间‌变换到‌世界空间‌,再与光照方向计算‌。

Shader “Custom/WorldSpaceNormalMap” {
Properties {
_MainTex (“Albedo”, 2D) = “white” {}
_NormalMap (“Normal Map”, 2D) = “bump” {}
_NormalScale (“Normal Scale”, Range(0,2)) = 1.0
}
SubShader {
Tags { “RenderType”=“Opaque” }

    Pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct appdata {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 tangent : TANGENT;
            float2 uv : TEXCOORD0;
        };
        
        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
            float3 worldPos : TEXCOORD1;
            // TBN矩阵相关向量
            float3 worldNormal : TEXCOORD2;
            float3 worldTangent : TEXCOORD3;
            float3 worldBitangent : TEXCOORD4;
        };
        
        sampler2D _MainTex, _NormalMap;
        float _NormalScale;
        
        v2f vert (appdata v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.uv;
            o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            
            // --- 使用Unity内置函数计算 ---
            // o.worldNormal = UnityObjectToWorldNormal(v.normal);
            // o.worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
            
            // --- 手动计算TBN矩阵 ---
            o.worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal);
            o.worldTangent = mul((float3x3)unity_ObjectToWorld, v.tangent.xyz);
            // 副切线计算(考虑手性与非均匀缩放)
            float3 worldBitangent = cross(o.worldNormal, o.worldTangent) * v.tangent.w * unity_WorldTransformParams.w;
            o.worldBitangent = worldBitangent;
            
            return o;
        }
        
        float4 frag (v2f i) : SV_Target {
            // 采样法线贴图并解包(切线空间→世界空间)
            float3 tangentNormal = UnpackNormal(tex2D(_NormalMap, i.uv));
            tangentNormal.xy *= _NormalScale;
            tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
            
            // 构建TBN矩阵
            float3x3 tbn = float3x3(
                normalize(i.worldTangent),
                normalize(i.worldBitangent),
                normalize(i.worldNormal)
            );
            
            // 将法线从切线空间转换到世界空间
            float3 worldNormal = mul(tbn, tangentNormal);
            
            // 光照计算(世界空间)
            float3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos);
            float diffuse = max(0, dot(worldNormal, lightDir));
            
            // 采样漫反射贴图
            float4 albedo = tex2D(_MainTex, i.uv);
            return albedo * diffuse;
        }
        ENDCG
    }
}

}

‌关键逻辑解析‌
‌顶点着色器阶段‌

‌TBN矩阵构建‌:
使用内置函数时,直接调用 UnityObjectToWorldNormal 和 UnityObjectToWorldDir 转换法线、切线到世界空间‌。
手动计算时,通过 mul 与 unity_ObjectToWorld 矩阵完成变换,并计算副切线(需处理非均匀缩放与手性)‌。
‌片元着色器阶段‌

‌法线解包‌:UnpackNormal 用于解码法线贴图(支持压缩格式)‌。
‌TBN矩阵应用‌:通过矩阵乘法将切线空间法线转换到世界空间‌。
‌光照计算‌:所有方向向量统一在世界空间下计算漫反射‌。
‌渐变纹理(Ramp Texture)的核心概念与实现‌
渐变纹理是一种通过‌预定义颜色过渡规则‌控制表面光照效果的纹理技术,主要用于实现‌非真实感渲染‌(如卡通风格、插画风格)‌13。其核心原理与关键应用如下:

三,渐变纹理

‌一、基本原理‌

‌替代传统漫反射计算‌

传统Lambert模型通过法线与光源方向点积计算明暗,而渐变纹理将计算结果映射到‌0~1区间‌,作为UV坐标采样渐变纹理颜色‌13。
公式:
glsl
Copy Code
float halfLambert = dot(normal, lightDir) * 0.5 + 0.5; // 映射到[0,1]
float3 rampColor = tex2D(_RampTex, float2(halfLambert, 0.5));
‌颜色可控性‌

设计师可通过调整渐变纹理的‌颜色过渡曲线‌,精确控制物体表面的明暗层次与色调变化,例如实现硬边阴影或冷暖色调过渡‌。
‌二、核心优势‌
‌风格化渲染支持‌
渐变纹理通过离散的颜色分界强化轮廓线,使模型呈现‌卡通插画风格‌(见图1)‌。
‌性能高效‌
仅需一次纹理采样即可替代复杂的光照计算,适用于移动端或低性能设备‌。

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 7/Ramp Texture" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_RampTex ("Ramp Tex", 2D) = "white" {}
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};
			
			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.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Use the texture to sample the diffuse color
				fixed halfLambert  = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
				fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
				
				fixed3 diffuse = _LightColor0.rgb * diffuseColor;
				
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

4,遮罩纹理

‌一、技术原理‌
‌遮罩作用机制‌

遮罩纹理存储灰度值(单通道)或RGBA多通道数据,在渲染过程中通过‌纹理采样‌获取特定位置的遮罩值(如 r 通道),并与表面属性(如高光强度、漫反射系数)进行‌乘法运算‌,实现区域化控制‌。
公式示例(控制高光强度):
glsl
Copy Code
float mask = tex2D(_MaskTex, uv).r;
float3 specular = _LightColor0.rgb * pow(max(0, dot(N, H)), _Gloss) * mask;
(mask 为遮罩值,0表示完全屏蔽高光,1表示完全保留)‌。
‌数据映射规则‌

灰度值范围(0~1)对应属性影响的衰减程度,例如:
‌0(黑色)‌:完全屏蔽效果(如无高光)
‌0.5(灰色)‌:中等强度
‌1(白色)‌:完全生效‌。

fixed4 frag(v2f i) : SV_Target {  
    // 采样基础颜色  
    fixed4 albedo = tex2D(_MainTex, i.uv);  
    
    // 计算光照方向与视角方向  
    float3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));  
    float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));  
    float3 halfDir = normalize(lightDir + viewDir);  
    
    // 采样遮罩纹理(R通道控制高光强度)  
    float mask = tex2D(_MaskTex, i.uv).r;  
    
    // 计算高光反射  
    float spec = pow(max(0, dot(i.worldNormal, halfDir)), _Gloss);  
    float3 specular = _LightColor0.rgb * _Specular.rgb * spec * mask;  // 遮罩值mask控制高光区域‌:ml-citation{ref="1,2" data="citationList"}  
    
    // 最终颜色混合  
    return fixed4(albedo.rgb + specular, 1.0);  
}  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot使用注解扫描注册Java Web三大组件

使用注解扫描和注册Java Web三大组件(Servlet、Filter、Listener)非常方便。 1. Servlet 注册 Servlet 是 Java Web 开发的基础组件,用于处理客户端(通常是浏览器)发送的 HTTP 请求并生成响应。 Controller是基于 Ser…

STM32F4 UDP组播通信:填一填ST官方HAL库的坑

先说写作本文的原因,由于开项目开发中需要用到UDP组播接收的功能,但是ST官方没有提供合适的参考,使用STM32CubeMX生成的代码也是不能直接使用的,而我在网上找了一大圈,也没有一个能够直接解决的方案,deepse…

JVM - 3.垃圾回收

1.垃圾收集的经典问题 1.哪些内存需要回收2.什么时候回收3.如何回收1.你知道哪几种垃圾回收器,各自的优缺点,重点讲一下cms和g12.JVM GC算法有哪些,目前的JDK版本采用什么回收算法3.G1回收器的回收过程 1.Java中垃圾的定义(Garbag…

重构谷粒商城09:人人开源框架的快速入门

谷粒商城09——人人开源框架的快速入门 前言:这个系列将使用最前沿的cursor作为辅助编程工具,来快速开发一些基础的编程项目。目的是为了在真实项目中,帮助初级程序员快速进阶,以最快的速度,效率,快速进阶…

css实现元素垂直居中显示的7种方式

文章目录 * [【一】知道居中元素的宽高](https://blog.csdn.net/weixin_41305441/article/details/89886846#_1) [absolute 负margin](https://blog.csdn.net/weixin_41305441/article/details/89886846#absolute__margin_2) [absolute margin auto](https://blog.csdn.net…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)

背景&#xff1a; 已经用这个脚本的记得设置Wifi时候&#xff0c;关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开&#xff0c;一天要重新连接&#xff0c;点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本&#xff0c;那样我就可以解放双手&…

双击PPT文件界面灰色不可用,需要再次打开该PPT文件才能正常打开

双击PPT文件界面灰色不可用&#xff0c;需要再次打开该PPT文件才能正常打开 1. 软件环境⚙️2. 问题描述&#x1f50d;3. 解决方法&#x1f421;解决步骤 4. 结果预览&#x1f914; 1. 软件环境⚙️ Windows10 或 Windows11 专业版64位&#xff0c;安装MotionGo软件&#xff08…

蓝桥杯[每日两题] 真题:好数 神奇闹钟 (java版)

题目一&#xff1a;好数 题目描述 一个整数如果按从低位到高位的顺序&#xff0c;奇数位&#xff08;个位、百位、万位 &#xff09;上的数字是奇数&#xff0c;偶数位&#xff08;十位、千位、十万位 &#xff09;上的数字是偶数&#xff0c;我们就称之为“好数”。给定…

蓝桥杯刷题周计划(第二周)

目录 前言题目一题目代码题解分析 题目二题目代码题解分析 题目三题目代码题解分析 题目四题目代码题解分析 题目五题目代码题解分析 题目六题目代码题解分析 题目七题目代码题解分析 题目八题目题解分析 题目九题目代码题解分析 题目十题目代码题解分析 题目十一题目代码题解分…

ThinkPHP框架

在电脑C磁盘中安装composer 命令 在电脑的D盘中创建cd文件夹 切换磁盘 创建tp框架 创建一个aa的网站&#xff0c;更换路径到上一步下载的tp框架路径 在管理中修改路径 下载压缩包public和view 将前面代码中的public和view文件替换 在PHPStom 中打开文件 运行指定路径 修改demo…

Spring学习笔记:工厂模式与反射机制实现解耦

1.什么是Spring? spring是一个开源轻量级的java开发应用框架&#xff0c;可以简化企业级应用开发 轻量级 1.轻量级(对于运行环境没有额外要求) 2.代码移植性高(不需要实现额外接口) JavaEE的解决方案 Spring更像是一种解决方案&#xff0c;对于控制层&#xff0c;它有Spring…

爬虫案例八js逆向爬取网易音乐

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、js逆向的前期准备二、网站分析三、代码 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬取网易音乐 提示&#xff1a;以下是本篇…

【02】Web网页基础

一、网页的组成 1、HTML 2、CSS 3、javascript 二、网页的结构 三、节点树及节点间的关系 四、选择器 一、网页的组成 网页可以分为三大部分 —— HTML、CSS 和 JavaScript。如果把网页比作一个人的话&#xff0c;HTML 相当于骨架&#xff0c;JavaScript 相当于肌肉&#…

Dify部署踩坑指南(Windows+Mac)

组件说明 Dify踩坑及解决方案 ⚠️ 除了修改镜像版本&#xff0c;nginx端口不要直接修改docker-compose.yaml &#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1、更换镜像版本 这个文件是由.env自动生成的&#xff0c;在.env配置 …

​​《从事件冒泡到处理:前端事件系统的“隐形逻辑”》

“那天在document见到你的第一眼&#xff0c;我就下定决心要陪你到天荒地老” ---React 我将从事件从出现到被处理的各个过程来介绍事件机制&#xff1a; 这张图片给我们展示了react事件的各个阶段&#xff0c;我们可以看到有DOM&#xff0c;合成事件层&#xff0c;还有…

tiktok web登录 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码 response reques…

【音视频】ffplay常用命令

一、 ffplay常用命令 -x width&#xff1a;强制显示宽度-y height&#xff1a;强制显示高度 强制以 640*360的宽高显示 ffplay 2.mp4 -x 640 -y 360 效果如下 -fs 全屏显示 ffplay -fs 2.mp4效果如下&#xff1a; -an 禁用音频&#xff08;不播放声音&#xff09;-vn 禁…

手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机

首先准备一个拓展坞 然后 插入一个外接的U盘 插入鼠标 插入有数字小键盘区的键盘 然后准备一根高清线&#xff0c;一端链接电脑显示器,一端插入拓展坞 把拓展坞的连接线&#xff0c;插入手机充电口&#xff08;可能会需要转接头&#xff09; 然后确保手机开机 按下键盘…

基于SpringBoot的“文物管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“文物管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体功能模块图 E-R实体图 系统首页界面 系统…