神临的Unity shader学习之漫反射(一)

本文介绍Unity Shader中实现漫反射的方法,包括Shader的基本结构、属性设置、光照模型引用等,并通过具体代码示例解释如何创建简单的漫反射效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写Shader程序有三种方式,一是固定管线(由于过于古老且无法发挥现代显卡的效果几乎被弃用了),二是经典的vertex,fragment算法,三是Unity自带的 surface shader(同时也是我推荐使用的一种方法,简单,易复用).
想学好unity shader 对于固定管线的理解可以说是所有的基础,其次就是基础的编写语言CG/LSGL了,然后依靠时间去慢慢的理解各种应用方案以及光学知识,数学基础等.不过个人感觉需要的数学知识并不难,真正难得是它的各种效果体系的复杂.
好了,我就不多说了,现在开始写我的第一篇复习,关于漫反射可以说是在生活中无处不在的效果了,所以想要有一个好的渲染效果,漫反射是离不开的.先上代码.

Shader"myDiffuse"
{
  Properties//(1)
  { 
  _MainTex("Base(RGB)",2D)="white"{}
  }

  SubShader//(2)
  {
    Tags{"RenderType"="Opaque"}//(3)
    Lod 200//(4)

    CGPROGRAM
    #pragma Surface surf Lambert//(5)

    sampler2D _MainTex;//(6)

    struct Input//(7)
    {
      float2 uv_MainTex//(8)
    };

    void surf(Input IN,inout SurfaceOutput o)//(9)
    {
      half4 c = tex2D(_MainTex,IN.uv_MainTex);//(10)
      o.Albedo = c.rgb;
      o.Alpha = c.a;
    }
    ENDCG
    }
  FallBack"Diffuse"//(11)
}

按照我程序里标好的序号,come on.
(1)这里是属性块,在这里建立程序和外部的连接,可以和轻松的调整渲染属性,如下图(随意找了一张图,懒得再截取了,请原谅神临的懒惰,嘿嘿):
这里写图片描述

(2)一个shader中至少有一个SubShader,其实就是专门针对某个设备进行编程的存在,本人向来只写一个,两个的时候很少,嗯….我承认自己很懒的…

(3)这个是标签,可以定义许多属性,至于有什么,很麻烦的,这里要记许多,感觉自己都没记清楚,至于是什么,可爱的你网上查吧.

(4)这个应该都很熟悉就对了,只不过我感觉这里的LOD和unity的LOD是两个东西,一个是在应用程序阶段进行改变,另一个是在图像编程的时候进行改变,对节省消耗有很大的意义,据说还可以利用这个完成一些奇奇怪怪的效果.

(5)这里是引用内建的Lembert光照模型(漫反射),想看看具体是啥可以去内置CG中去看看,这个是基础一定要看的哦.下面是内建光照模型,这里贴出来,Lighting是人家的格式要求,必须要写的,神临真想说,要是我设计的shader,一定要把这个Lighting去掉,好麻烦的.不过现在还是扎扎实实的学吧,任重而道远!!
这里写图片描述
至于这里的计算其实就是利用了dot函数里 cos 的角度越大值越小的效果计算出来的,具体的可以在网上找找哦.

(6)建立变量,要和属性的命名一致哦,否则没办法引用窗口上的属性.

(7)输入输出结构体,能够将程序中得到的参数传递到着色器上,从而参与计算,里面的参数是有规定的,同样的,记得去看内置CG中的具体内容.

(8)映射贴图的UV坐标,只有这样,在后续才能采样后准确的赋予某个物体贴图颜色.

(9)表面函数,从INPUT中接收到的数据,要经过这里的计算转入光照模型中才能得到效果.

(10)贴图采样计算,这里unity已经帮我们建立好了函数,只要大胆的用tex2D方法就行了,省去了很大写重复代码的时间

(11)这里是一个回调同时也是一个补充,当写的所有shader都不能执行,或者缺少比如阴影之类的东西时,它会起到保护和补充的作用.

好了,就先到这里了,下次再继续,本人要下班了,虽说上班时间写这个被老板发现的后果很严重,但是手痒痒就写了,吾辈中人,应当敢于面对惨淡的人生!!!

<think>好的,我现在需要解决用户关于Unity Shader逐顶点漫反射出现黑色的问题。首先,我得回想下可能的原因。用户提到的是逐顶点计算,所以问题可能出在顶点着色器阶段。常见的导致黑色的原因包括法线方向不正确、光照计算错误或者坐标空间不致。 首先,检查法线是否正确转换到世界空间。在顶点着色器中,如果直接使用模型空间的法线而不进行转换,可能会导致光照计算错误。应该使用UnityObjectToWorldNormal函数将法线从模型空间转到世界空间,并且需要归化处理。另外,光照方向是否正确也很重要,通常使用_WorldSpaceLightPos0.xyz获取光源方向,但要注意是否是平行光。然后,考虑光照计算中的点积结果。如果法线和光源方向的点积为负值,而max函数将其截断到0,可能导致部分区域完全黑暗。用户提到的半兰伯特光照模型可能可以缓解这个问题,将点积结果从[-1,1]映射到[0,1],避免完全黑色区域。接下来是坐标空间的问题。顶点着色器中的法线和光源方向必须在同空间下计算,通常选择世界空间。如果法线在模型空间,而光源方向在世界空间,点积就会出错,导致错误的光照结果。另外,材质属性是否正确定义和传递也需要检查。例如,颜色属性_Color是否正确声明并在Shader中使用,是否有拼写错误或者缺少语义。还有,顶点着色器的输出结构是否正确。如果法线信息没有正确传递给片段着色器,或者插值出现问题,可能导致插值后的法线不正确,从而在片段阶段出现错误。 现在,解决方案应该包括正确转换法线到世界空间,使用半兰伯特模型,确保坐标空间致,以及检查属性声明。然后提供个完整的代码示例,展示如何正确实现逐顶点的漫反射,避免黑色区域的出现。 最后,生成相关问题,帮助用户深入理解可能相关的主题,比如逐顶点和逐像素的区别、半兰伯特的原理、法线转换的注意事项等。</think>### Unity Shader逐顶点漫反射出现黑色的原因及解决方案 #### 原因分析 1. **法线方向未归化** 顶点着色器中未对转换后的法线进行归化处理,导致点积计算错误。例如: $$dot(N_{world}, L_{world})$$ 若法线未归化,计算结果可能超出[-1,1]范围[^4]。 2. **光照方向与法线空间不致** 法线使用模型空间坐标,而光照方向使用世界空间坐标,导致点积计算无效[^3]。 3. **负值光照强度截断** 传统Lambert公式使用`max(0, dot(normal, lightDir))`,当法线与光线方向夹角大于90度时结果为0,导致黑色区域[^2]。 4. **顶点数据插值问题** 逐顶点计算时,若顶点法线存在突变,插值后的片段法线可能产生异常值[^1]。 --- #### 完整解决方案代码 ```glsl Shader "Custom/VertexDiffuse_HalfLambert" { Properties { _Color ("Main Color", Color) = (1,1,1,1) } SubShader { Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 color : COLOR; }; float4 _Color; v2f vert (appdata v) { v2f o; // 坐标转换 o.pos = UnityObjectToClipPos(v.vertex); // 法线世界空间转换 float3 worldNormal = UnityObjectToWorldNormal(v.normal); worldNormal = normalize(worldNormal); // 光源方向 float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); // 半兰伯特光照计算 float halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5; // 环境光+漫反射 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; float3 diffuse = _LightColor0.rgb * _Color.rgb * halfLambert; o.color = ambient + diffuse; return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(i.color, 1.0); } ENDCG } } FallBack "Diffuse" } ``` --- #### 关键改进点 1. **法线空间转换** 使用`UnityObjectToWorldNormal`代替直接使用模型空间法线,确保法线方向正确[^4]。 2. **半兰伯特光照模型** 将光照计算从传统Lambert改为: $$halfLambert = (dot(N, L) \times 0.5) + 0.5$$ 避免完全黑色区域[^2]。 3. **光照方向归化** 对`_WorldSpaceLightPos0`进行归化处理,确保方向向量单位化。 4. **环境光叠加** 添加`UNITY_LIGHTMODEL_AMBIENT`环境光补偿,避免纯暗场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值