Unity Shader-兰伯特光照模型与Diffuse Shader

UnityShader 基础光照模型-完整版(The Basic Lighting Model) 本文分别介绍了两种漫反射光照模型(兰伯特和半兰伯特)、两种镜面反射模型(Phong和Blinn-phong)自发光、环境光等理论基础和在unity中的计算方式。详细介绍了这些光照在逐顶点光照模型中和逐像素光照模型中的完整计算。附带完整代码和最终渲染图。解析逐顶点光照模型和逐像素光照模型的区别。一、基础光照模型(在基础光照模型中,物体的表面颜色是自发光、环境光、漫反射、镜面反射的总和。即surfaceColor = emissive + ambient + diffuse + specula。展示了实现过程。 阅读详情

简介


学了一段时间shader,然而一直在玩后处理,现在终于下定决心钻研一下真正的带光照的shader。从Diffuse到Specular。一个游戏的画面好坏,很大程度上取决于光照和贴图。现实世界中,我们之所以能看见东西,是因为他们要么反射了光源发出的光,要么是自身能够发光。而在游戏世界中,如果没有了光,我们虽然可以直接根据贴图显示物体的材质,但是少了很多细节光影效果,游戏显得不真实。但是,真实的光照计算是一个非常复杂的过程,对于游戏这种至少30FPS的程序来说是完全不可能的,所以我们必须要使用一种近似的光照算法,来模拟光照效果。本篇文章就来学习一下基本的光照模型以及其在unity下的shader实现。

漫反射和镜面反射


我们观察世界是因为有光进入我们的眼睛,光在世界中主要有反射和折射两种属性,当光照在某种介质表面时,一部分光发生反射,另一部分光进入介质,发生折射,也有转化为其他能量的光。本篇文章只讨论反射,折射等其他现象以后再学习。光的反射分为两种,漫反射和镜面反射。

漫反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。很多物体,如植物、墙壁、衣服等,其表面粗看起来似乎是平滑,但用放大镜仔细观察,就会看到其表面是凹凸不平的,所以本来是平行的太阳光被这些表面反射后,弥漫地射向不同方向。

镜面反射,是指若反射面比较光滑,当平行入射的光线射到这个反射面时,仍会平行地向一个方向反射出来,这种反射就属于镜面反射,其反射波的方向与反射平面的法线夹角(反射角),与入射波方向与该反射平面法线的夹角(入射角)相等,且入射波、反射波,及平面法线同处于一个平面内。


兰伯特光照模型


先来学习一个最简单的光照模型,兰伯特光照模型。兰伯特光照模型是目前最简单通用的模拟漫反射的光照模型,定义如下:模型表面的明亮度直接取决于光线向量(light vector)和表面法线(normal)两个向量将夹角的余弦值。光线向量是指这个点到光从哪个方向射入,表面法线则定义了这个表面的朝向。


如果漫反射光强设置为Diffuse,入射光光强为I,光方向和法线夹角为θ,那么兰伯特光照模型可以用下面的公式表示:Diffuse = I * cosθ
进一步地,我们可以通过点乘来求得两个方向向量之间的夹角,入射光方向设置为L,法线方向设置为N,如果光方向向量和法线方向向量都为单位向量(这就是为什么我们在写shader的时候需要normalize操作的原因),那么它们之间的夹角余弦值就可以表示为:cosθ = dot(L,N),最终漫反射光强公式,也就是兰伯特光照模型可以表示为:Diffuse = I  * dot(L,N)


逐顶点计算着色shader


我们在shader中需要计算输出的颜色,逐顶点着色也就是说我们的计算主要放在了vertex shader中,根据顶点来计算,每个顶点中计算出了该点的颜色,直接作为vertex shader的输出,pixel(fragment) shader的输入,当到达pixel阶段时,直接输出顶点shader的结果。比如一个三角形面片,在vertex阶段,分别计算了每个顶点的颜色值,在pixel阶段时,这个面片经过投影,最终显示在屏幕上的像素,会根据该像素周围的顶点来插值计算像素的最终颜色,这种着色方式也叫做 高洛德着色。

下面看一下unity shader实现的逐顶点着色:
    Shader "ApcShader/DiffusePerVetex"  
    {  
        //属性  
        Properties{  
            _Diffuse("Diffuse", Color) = (1,1,1,1)  
        }  
      
        //子着色器    
        SubShader  
        {  
            Pass  
            {  
                //定义Tags  
                Tags{ "RenderType" = "Opaque" }  
      
                CGPROGRAM  
                //引入头文件  
                #include "Lighting.cginc"  
                //定义Properties中的变量  
                fixed4 _Diffuse;  
                //定义结构体:应用阶段到vertex shader阶段的数据,如果定义了  
                struct a2v  
                {  
                    float4 vertex : POSITION;  
                    float3 normal : NORMAL;  
                };  
                //定义结构体:vertex shader阶段输出的内容  
                struct v2f  
                {  
                    float4 pos : SV_POSITION;  
                    fixed4 color : COLOR;  
                };  
      
                //定义顶点shader  
                v2f vert(a2v v)  
                {  
                    v2f o;  
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                    //把法线转化到世界空间  
                    float3 worldNormal = mul(v.normal, (float3x3)_World2Object);  
                    //归一化法线  
                    worldNormal = normalize(worldNormal);  
                    //把光照方向归一化  
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                    //根据兰伯特模型计算顶点的光照信息,dot可能有负值,小于0的部分可以理解为看不见,直接取0  
                    fixed3 lambert = max(0.0, dot(worldNormal, worldLightDir));  
                    //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                    o.color = fixed4(lambert * _Diffuse.xyz * _LightColor0.xyz, 1.0);  
                    return o;  
                }  
      
                //定义片元shader  
                fixed4 frag(v2f i) : SV_Target  
                {  
                    return i.color;  
                }  
      
                //使用vert函数和frag函数  
                #pragma vertex vert  
                #pragma fragment frag     
      
                ENDCG  
            }  
      
        }  
        //前面的Shader失效的话,使用默认的Diffuse  
        FallBack "Diffuse"  
    }  

我们放置两个基本几何体,看一下shader的效果:


逐像素计算着色shader


逐像素计算时,我们的主要计算放到了pixel shader里,在vertex shader阶段只是进行了基本的顶点变换操作,以及顶点的法线转化到世界空间的操作,然后将转化后的法线作为参数传递给pixel shader。其他的计算都放到了pixel shader阶段,这样,针对每个像素,我们都可以来计算这个像素的光照情况,而不是像逐顶点计算时,先计算好顶点的颜色,然后差值得到中间的像素颜色。这种逐像素着色的方式也叫作 冯氏着色(注意不是冯氏光照模型,不要搞混呦)。

下面看一下unity shader实现的逐像素着色:
    Shader "ApcShader/DiffusePerPixel"  
    {  
        //属性  
        Properties{  
            _Diffuse("Diffuse", Color) = (1,1,1,1)  
        }  
      
        //子着色器    
        SubShader  
        {  
            Pass  
            {  
                //定义Tags  
                Tags{ "RenderType" = "Opaque" }  
      
                CGPROGRAM  
                //引入头文件  
                #include "Lighting.cginc"  
                //定义Properties中的变量  
                fixed4 _Diffuse;  
                //定义结构体:应用阶段到vertex shader阶段的数据  
                struct a2v  
                {  
                    float4 vertex : POSITION;  
                    float3 normal : NORMAL;  
                };  
                //定义结构体:vertex shader阶段输出的内容  
                struct v2f  
                {  
                    float4 pos : SV_POSITION;  
                    float3 worldNormal : TEXCOORD0;  
                };  
      
                //定义顶点shader  
                v2f vert(a2v v)  
                {  
                    v2f o;  
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                    //把法线转化到世界空间  
                    o.worldNormal = mul(v.normal, (float3x3)_World2Object);  
                    return o;  
                }  
      
                //定义片元shader  
                fixed4 frag(v2f i) : SV_Target  
                {  
                    //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
                    fixed3 worldNormal = normalize(i.worldNormal);  
                    //把光照方向归一化  
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                    //根据兰伯特模型计算像素的光照信息,小于0的部分理解为看不见,置为0  
                    fixed3 lambert = max(0.0, dot(worldNormal, worldLightDir));  
                    //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                    fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz;  
                    return fixed4(diffuse, 1.0);  
                }  
      
                //使用vert函数和frag函数  
                #pragma vertex vert  
                #pragma fragment frag     
      
                ENDCG  
            }  
      
        }  
            //前面的Shader失效的话,使用默认的Diffuse  
            FallBack "Diffuse"  
    }  

还是一个立方体和一个圆柱体,采用了逐像素着色后的结果:



从vertex阶段到fragment阶段发生了什么


我们可以看一下逐顶点着色和逐像素着色的结果对比:

对于正方体,只有单个面,没有特别明显的差别,但是对于圆柱体,就可以看出一些差别了,逐顶点着色的圆柱体可以看出线条状的轮廓,其实每个线条都是由两个三角形面片组成的长方形面片。

为什么逐像素计算会得到更好的效果,因为我们逐像素取的光照的方向是一致的,法线方向也是通过上一步的vertex shader传递过来的,如果像素和顶点对应了的话,那不是每个像素的计算结果都会一样呢?然而,其实像素和顶点是不对应的,这个就是传说中的渲染流水线了,在顶点阶段计算的结果,并不是直接传递给像素着色器的,而是经过了一系列的插值计算,我们从vertex shader传递过来的法线方向,只代表了这一个顶点的顶点法线方向,而到了pixel阶段,这个像素所对应的法线等参数相当于其周围几个顶点进行插值后的结果。我们用这一个像素点对应的法线方向与光照方向进行计算,就可以获得该像素点在光照条件下的颜色值,而不是先计算好颜色再插值得到结果。







半兰伯特光照模型


实现了逐顶点和逐像素的兰伯特光照模型,我们再来看一下兰伯特光照模型的变种--半兰伯特光照。经过上面的对比,逐像素光照计算会获得更好的效果,所以我们下面就采用逐像素的方式来实现半兰伯特光照模型。

上面的shader计算光照的时候,我们计算法线方向和光方向的点乘值时,得到的结果有可能是负数,而兰伯特光照模型对于该情况的处理是,dot值为负数,说明该点不会受到光的照射,所以对于该光源,该点无光,直接使用max(0,diffuse)来将不应该受光的位置全都置为黑色。虽然听起来很有道理的样子,然而这种并好看。



然而,实际上,我们在现实世界中经常会发现,即使我们让一个物体不被光直接照射,我们也可能会看到物体,虽然亮度不是很高,这其实是由于物体之间光的反射造成的,也就是间接光照,间接光照是更高级的渲染,比如光线追踪算法等。但是在实时图形学,我们大部分情况是通过一个环境光(Ambient Light)统一代表了间接光,这样,即使在没有光的时候,我们也可以看见物体。

兰伯特光照出来的时候,貌似还没有这么高科技的技术,所以呢,有人就想到了一个取巧的技术(据说是《半条命》),既保证了兰伯特模型计算出来的光照结果大于0,又整体提升了亮度,使非直接受光面不是单纯的置为黑色。这是一个在图形学领域经常有的变换,区间转化,从(-1,1)转化到(0,1),如果不考虑无意义的负值,也可以说成从(0,1)转化到了(0.5,1)。方法很简单,乘以0.5再加上0.5。这样,原本亮度为1的地方,乘以0.5变成了0.5,加上0.5就又成了1,而原本光照强度为0的地方,就变成了0.5,原本为负数的地方,也能保证为大于0了。半兰伯特光照这种区间转化的原理图如下所示:


下面看一下逐像素计算的半兰伯特光照shader,比兰伯特光照的只是将法线向量与光方向向量的点乘结果用一种更好的方式区间转化到了(0,1)区间:

Shader "ApcShader/HalfLambert"  
{  
    //属性  
    Properties{  
        _Diffuse("Diffuse", Color) = (1,1,1,1)  
    }  
  
    //子着色器    
    SubShader  
    {  
        Pass  
        {  
            //定义Tags  
            Tags{ "RenderType" = "Opaque" }  
  
            CGPROGRAM  
            //引入头文件  
            #include "Lighting.cginc"  
            //定义Properties中的变量  
            fixed4 _Diffuse;  
            //定义结构体:应用阶段到vertex shader阶段的数据  
            struct a2v  
            {  
                float4 vertex : POSITION;  
                float3 normal : NORMAL;  
            };  
            //定义结构体:vertex shader阶段输出的内容  
            struct v2f  
            {  
                float4 pos : SV_POSITION;  
                float3 worldNormal : TEXCOORD0;  
            };  
  
            //定义顶点shader  
            v2f vert(a2v v)  
            {  
                v2f o;  
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                //把法线转化到世界空间  
                o.worldNormal = mul(v.normal, (float3x3)_World2Object);  
                return o;  
            }  
  
            //定义片元shader  
            fixed4 frag(v2f i) : SV_Target  
            {  
                //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
                fixed3 worldNormal = normalize(i.worldNormal);  
                //把光照方向归一化  
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                //半兰伯特光照,将原来(-1,1)区间的光照条件转化到了(0,1)区间,既保证了结果的正确,又整体提升了亮度,保证非受光面也能有光,而不是全黑  
                fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;  
                //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz;  
                return fixed4(diffuse, 1.0);  
            }  
  
            //使用vert函数和frag函数  
            #pragma vertex vert  
            #pragma fragment frag     
  
            ENDCG  
        }  
  
    }  
    //前面的Shader失效的话,使用默认的Diffuse  
    FallBack "Diffuse"  
}  

看一下兰伯特光照模型和半兰伯特光照模型的对比:


所以,正如某图形学大牛说的,图形学这个,没有什么道理,只要看起来好看,那就行了!


带有纹理的半兰伯特光照shader


光照模型再牛,没有纹理也是难看,所以,我们修改一下shader,加上纹理,然后找个帅帅哒模型穿上瞧一瞧。
光照计算主要放在Fragment shader中:

    Shader "ApcShader/DiffuseWithTex"  
    {  
        //属性  
        Properties{  
            _Diffuse("Diffuse", Color) = (1,1,1,1)  
            _MainTex("Base 2D", 2D) = "white"{}  
        }  
      
        //子着色器    
        SubShader  
        {  
            Pass  
            {  
                //定义Tags  
                Tags{ "RenderType" = "Opaque" }  
      
                CGPROGRAM  
                //引入头文件  
                #include "Lighting.cginc"  
                //定义Properties中的变量  
                fixed4 _Diffuse;  
                sampler2D _MainTex;  
                //使用了TRANSFROM_TEX宏就需要定义XXX_ST  
                float4 _MainTex_ST;  
      
                //定义结构体:应用阶段到vertex shader阶段的数据  
                struct a2v  
                {  
                    float4 vertex : POSITION;  
                    float3 normal : NORMAL;  
                    float4 texcoord : TEXCOORD0;  
                };  
                //定义结构体:vertex shader阶段输出的内容  
                struct v2f  
                {  
                    float4 pos : SV_POSITION;  
                    float3 worldNormal : TEXCOORD0;  
                    //转化纹理坐标  
                    float2 uv : TEXCOORD1;  
                };  
      
                //定义顶点shader  
                v2f vert(a2v v)  
                {  
                    v2f o;  
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                    //把法线转化到世界空间  
                    o.worldNormal = mul(v.normal, (float3x3)_World2Object);  
                    //通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.xy;  
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);  
                    return o;  
                }  
      
                //定义片元shader  
                fixed4 frag(v2f i) : SV_Target  
                {  
                    //unity自身的diffuse也是带了环境光,这里我们也增加一下环境光  
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz;  
                    //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
                    fixed3 worldNormal = normalize(i.worldNormal);  
                    //把光照方向归一化  
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                    //根据半兰伯特模型计算像素的光照信息  
                    fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;  
                    //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                    fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz + ambient;  
                    //进行纹理采样  
                    fixed4 color = tex2D(_MainTex, i.uv);  
                    return fixed4(diffuse * color.rgb, 1.0);  
                }  
      
                //使用vert函数和frag函数  
                #pragma vertex vert  
                #pragma fragment frag     
      
                ENDCG  
            }  
      
        }  
            //前面的Shader失效的话,使用默认的Diffuse  
            FallBack "Diffuse"  
    }  

光照计算主要放在vertex shader中:

    Shader "ApcShader/DiffuseWithTexX"  
    {  
        //属性  
        Properties{  
            _Diffuse("Diffuse", Color) = (1,1,1,1)  
            _MainTex("Base 2D", 2D) = "white"{}  
        }  
      
        //子着色器    
        SubShader  
        {  
            Pass  
            {  
                //定义Tags  
                Tags{ "RenderType" = "Opaque" }  
      
                CGPROGRAM  
                //引入头文件  
                #include "Lighting.cginc"  
                //定义Properties中的变量  
                fixed4 _Diffuse;  
                sampler2D _MainTex;  
                //使用了TRANSFROM_TEX宏就需要定义XXX_ST  
                float4 _MainTex_ST;  
                //定义结构体:应用阶段到vertex shader阶段的数据,如果定义了  
                struct a2v  
                {  
                    float4 vertex : POSITION;  
                    float3 normal : NORMAL;  
                    float4 texcoord : TEXCOORD0;  
                };  
                //定义结构体:vertex shader阶段输出的内容  
                struct v2f  
                {  
                    float4 pos : SV_POSITION;  
                    fixed4 color : COLOR;  
                    //转化纹理坐标  
                    float2 uv : TEXCOORD1;  
                };  
      
                //定义顶点shader  
                v2f vert(a2v v)  
                {  
                    v2f o;  
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                    //unity自身的diffuse也是带了环境光,这里我们也增加一下环境光  
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz;  
                    //把法线转化到世界空间  
                    float3 worldNormal = mul(v.normal, (float3x3)_World2Object);  
                    //归一化法线  
                    worldNormal = normalize(worldNormal);  
                    //把光照方向归一化  
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                    //根据兰伯特模型计算顶点的光照信息,dot可能有负值,小于0的部分可以理解为看不见,直接取0  
                    fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;  
                    //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                    o.color = fixed4(lambert * _Diffuse.xyz * _LightColor0.xyz + ambient, 1.0);  
                    //通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.xy;  
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);  
                    return o;  
                }  
      
                //定义片元shader  
                fixed4 frag(v2f i) : SV_Target  
                {  
                    return i.color * tex2D(_MainTex, i.uv);  
                }  
      
                //使用vert函数和frag函数  
                #pragma vertex vert  
                #pragma fragment frag     
      
                ENDCG  
            }  
      
        }  
        //前面的Shader失效的话,使用默认的Diffuse  
        FallBack "Diffuse"  
    }  

我们用一个人物模型,分别使用两种shader,进行一下对比,左侧的shader主要计算在vertex,右侧的shader主要计算放在pixel:

可以看出,如果模型比较细致,其实在diffuse情况下,是没有特别明显的区别的,而大部分计算放在vertex shader中,对于效率更有益处,vertex shader一般不是GPU的瓶颈,逐顶点计算可以比逐像素计算省很多,所以将尽可能多的计算放在vertex阶段而不是fragment阶段是一个很好的优化shader的策略。但是,注意!是在diffuse的情况,如果我们的shader中有高光specular,那么,用逐顶点计算高光就会出现特别难看的光斑,这个下篇文章再进行介绍。

由于unity shader中diffuse是带有环境光的,所以我们也在shader中计算了环境光。由于没有全局光照,所以间接光照就通过UNITY_LIGHTMODEL_AMBIENT这个宏进行访问

TRANSFORM_TEX宏


在添加了纹理之后,主要使用了一个宏和一个采样函数。采样函数顾名思义,tex2D,就是通过传入的纹理坐标,来获得纹理采样点所对应的颜色值。下面重点看一下Unity为我们提供的TRANSFORM_TEX宏,我们从UnityCG.cginc中可以找到这个宏的定义如下:

    // Transforms 2D UV by scale/bias property  
    #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)  

如果我们使用了这个宏,就需要在shader中定义我们要采样的纹理的一个系数,命名方式为 纹理名_ST,float4类型。那么这个值是什么呢?
就是这个啦!我们在使用纹理时,unity会为我们提供两个参数,一个是Tiling,一个是Offset。简单来说,Tiling表示纹理的缩放比例,Offset表示了纹理使用时采样的偏移值。关于Tiling和Offset的介绍,可以参考 这篇文章。知道了这个,也就好理解TRANSFORM_TEX宏所做的事情了,在采样时,将材质面板上设置的Tiling值通过XXX_ST.xy传递进来,用于和采样的坐标相乘,进行采样的缩放,将Offset值通过XXX_ST.zw传递进来,作为纹理采样的偏移。

原文地址:http://blog.youkuaiyun.com/puppet_master/article/details/53074789

【Unity Shader】 Lambert(兰伯特)光照模型 Unity Shader Lambert光照模型与漫反射 一、漫反射简介 漫反射的特点 二、漫反射光照模型-Lambert(兰伯特)光照模型 Lambert 余弦定理 三、自定义UnityShader实现漫反射(Diffuse)效果 写法 效果展示 参考 Unity Shader Lambert光照模型与漫反射 一、漫反射简介 一束平行光入射到凹凸不平的表面... 阅读详情

相关推荐

Unity Shader-Phong光照模型与Specular

简介 学完了兰伯特光照模型,再来学习一个更加高级一点的光照模型-Phong光照模型。兰伯特光照模型中主要就是漫反射,而实际上,光除了漫反射,还有镜面反射。一些金属类型的材质,往往表现出一种高光效果,用兰伯特模型是模拟不出来的,所以就有了Phong模型。Phong模型主要有三部分构成,第一部分是上一篇中介绍了的Diffuse,也就是漫反射,第二部分是环境光,在非全局光照的情况下,我们一般是

puppet_master的专栏 1万+

Unity Shader Diffuse

Shader "Custom/MyDiffuse" { SubShader { pass{ tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "unitycg.cginc" #include "lightin

Var-al 的博客 4149

【Unity Shader】(1)兰伯特模型 逐顶点光照 和 逐像素光照 分别实现 漫反射光照模型

使用逐顶点光照在unityShader中实现漫反射效果 漫反射公式 首先需要了解基本光照模型中,漫反射(Diffuse) 其中共有4个参数 入射光线的颜色和强度—— C light 材质的漫反射系数—— M diffuse 表面法线—— n 光源方向—— I 为了避免 n 和 I 的点积为负值,我们需要使用max来操作。但在Unity Shader中,我们可以使用 saturate(x)函数达到相同的效果。 其中,x可以是操作的标量或矢量,即float、float2、float3等类型 satura

1eonChan的博客 1909

Shader-Diffuse

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel...

weixin_42045212的博客 622

【Unity Shaders】概述及Diffuse Shading介绍

概述 几个月以前我就一直在找关于Unity Shader相关的资料,大三的时候有一门叫《计算机图形学》,让我觉得如果游戏程序员要从beginner成长为advancer,就必须了解底层渲染的一些知识。 Unity3D无疑是一个非常出色的游戏引擎,它让很多独立开发者真正能够独立开发一款让自己满意的游戏,而不需要考虑那些烦人的细节,正如Unity宣称的那样,让他们来做那些令人抓狂的事情吧!

candycat 2万+

在Unity Shader实现半兰伯特光照模型

Shader "Unity Shaders Book/Chapter 6/HalfLambert" { Properties { _Diffuse ("Diffuse", Color)= (1, 1, 1, 1)//声明一个Color类型的属性,并把它初始值设为白色 } SubShader{ Pass{ //LightMode标签是Pass标签的一种,它用于定义该Pass在...

夏季洋流的博客 781

[Unity-Shader]标准光照模型-漫反射(diffuse)

学习资料来自《Shader入门精要》 漫反射 漫反射光照是用于对那些被物体表面随机散射到各个方向的辐射度进行建模的。在漫反射中,视角的位置是不重要的,因为反射是完全随机的,因此可以认为在任何反射方向上的分布都是一样的。但是入射光线的角度很重要。 漫反射光照符合兰伯特定律:反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。因此,漫反射部分计算如下: Cdiffuse=Clight⋅m...

隔壁老王三金的博客 1705

CMake、Make、MinGw、Clang、Llvm、GCC、MSVC的区别

除开 GCC (GNU 编译器集合) 以外,MinGW 还包含有一些其他的 GNU 程序开发工具;的缩写,MinGw是windows版本的gcc集合。并不仅是一个 C/C++ 编译器,而是一套。是微软开发的VC运行时库,3、Make和CMake是。

m0_37698164的博客 1万+

shader入门之基础光照知识

随着汽车电子行业的发展,自动驾驶和智能驾驶舱越来越成熟,人机交互界面HMI(Human Machine Interface)也越来越炫酷,这对于HMI领域来说是一个挑战,比如:如何调试出效果精美的车辆模型?如何更直观、流畅的显示周围行车环境?这些都涉及到丰富的计算机图形学的相关知识,今天这篇文章就给大家简单介绍基础光照的相关知识。两个向量的点乘等于他们的数乘结果乘以两个向量之间夹角的余弦值,公式如下:这非常有用,两个向量之间的夹角记作θ,如果将向量化为单位向量后,它们的长度等于1,那么公式简化成:从公式可以

怿星科技的博客 839

模型量化必知:Per-Tensor和Per-Channel到底怎么选?

本文深入解析了模型量化中Per-Tensor与Per-Channel两种核心策略的抉择。Per-Tensor量化实现简单、硬件兼容性极佳,适合激活值或分布均匀的权重;而Per-Channel量化精度更高,能更好地处理权重分布不均的卷积层,是现代CNN权重量化的主流。文章提供了从硬件平台、数值分布到精度需求的实战决策框架,并推荐了“权重Per-Channel,激活Per-Tensor”的混合量化最佳实践。

assembly8low的博客 983

使用mmdetection快速复现计算机视觉顶会论文

使用mmdetection中支持的算法运行自定义数据集

m0_56896669的博客 2422

Unity_Shader学习笔记_01_关于漫反射和高光反射等基本的计算.shader入门精要

输出颜色由几个部分组成,一个是漫反射(Diffuse),一个是高光反射(Specular),还有环境光遮蔽(Ambient)等。 漫反射(Diffuse)由基本颜色和灯光颜色,灯光模型计算得出。 fixed3 diffuse = _LightColor0.rgbalbedomax(0, dot(worldNormal, worldLightDir)); _LightColor0.rgb:灯光颜色 ...

bin1479的博客 436

用R搞定生态溯源:MixSIAR贝叶斯混合模型从安装到出图的保姆级教程

本文提供了一份详细的MixSIAR贝叶斯混合模型教程,涵盖从R环境配置、数据预处理到模型参数设置和可视化分析的完整流程。通过实战案例和常见问题解决方案,帮助生态学研究者快速掌握混合模型在生态溯源中的应用,提升数据分析效率。

weixin_42531516的博客 466

UnityShader---漫反射Shader(内置渲染管线)---5

顶点漫反射: Shader "Unlit/005" { Properties { _Diffuse("Diffuse", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include

weixin_41155760的博客 137

鸿蒙OS开机动画背后的秘密:从代码到视觉的完整解析(附源码分析)

本文深入解析了鸿蒙OS开机动画的系统级实现原理。从bootanimation服务的启动配置、与Render Service的协同,到基于VSync信号的帧率同步与绘制流水线,结合OpenHarmony源码进行了逐行解读。文章揭示了其事件驱动的优雅退出机制,并探讨了视频支持、多屏适配等高级特性与定制化可能,为开发者理解鸿蒙图形子系统提供了硬核技术视角。

sony5的博客 972

基于STM32的智能台灯PWM调光与人体感应融合设计

本文详细介绍了基于STM32单片机的智能台灯设计方案,重点讲解了PWM调光技术与人体感应模块的融合实现。通过STM32的高级定时器产生精准PWM信号控制LED亮度,结合红外传感器实现人来灯亮、人走灯灭的智能体验,同时集成光敏传感实现环境自适应调光,打造高效节能的智能照明解决方案。

threejs5artist的博客 823

Unity 2019.3.2 + ShaderForge:美术同学的第一行Shader代码(从结构体到半兰伯特)

本文介绍了如何使用Unity 2019.3.2和ShaderForge为美术同学创建第一个Shader效果,从结构体到半兰伯特材质的实现。通过可视化节点连接,无需编程基础即可完成可交互材质球,提升3D模型的立体感和光照效果。适合技术美术和游戏开发者快速上手Shader创作。

weixin_29534421的博客 287

论文复现《基于最大李雅普诺夫指数的电力系统暂态电压稳定评估方案》

电力系统暂态电压稳定评估方案 摘要 本研究提出了一种基于最大李雅普诺夫指数的电力系统暂态电压稳定评估方法。通过分析相空间中相邻轨迹的收敛或发散特性,该方法能够提前判别系统稳定性。论文首先阐述了暂态电压稳定机理,详细介绍了最大李雅普诺夫指数的计算方法。针对IEEE-39节点系统,建立了仿真模型并获取不同故障场景下的数据。研究开发了完整的评估程序,包括数据预处理、相空间重构、指数计算等模块,并通过与时域仿真结果的对比验证了方法的准确性。实验结果表明,该方法能够快速有效地评估电力系统的暂态电压稳定性,为系统安全运

max500600的博客 187

【Unity Shaders】Diffuse Shading——漫反射光照改善技巧

本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。 这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。 ========================================== 分割线==========================...

weixin_33966095的博客 455

使用C++实现DNS欺骗攻击

其中,domain_name表示需要欺骗的域名,dns_server_ip表示实际DNS服务器的IP地址,fake_ip表示需要重定向到的IP地址。在DNS查询过程中,当客户端向DNS服务器请求解析某个域名时,DNS服务器会返回该域名对应的IP地址。DNS劫持是一种常见的网络攻击方式,通过篡改DNS响应数据,使得用户访问的网站被重定向到攻击者指定的恶意站点。我们使用C++编写一个简单的DNS欺骗程序,使得当客户端请求解析特定的域名时,我们可以将其重定向到指定的IP地址。使用C++实现DNS欺骗攻击。

TechGlide的博客 411

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【shader学习记录】半兰伯特材质和逐顶点/逐像素漫反射

       逐顶点/像素材质的阴影表现很单调。是因为使用了saturate函数将dot(wordNormal,wordLight)的值截取到[0,1],相比之下半兰伯特阴影表现较为柔和。           因为半兰伯特在计算中没有舍弃掉[0,1]以外的值,而通过dot(wordNormal,wordLight)*x+y的方式计算,其中x,y一般都为0.5。但是修改过几次后发现,x值越小,对比度...

楔子1996的博客 593

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Diffuse Shading——漫反射光照改善技巧

转:http://www.narkii.com/club/thread-355113-1.html 我们会列出两种方法:使用Half Lambert lighting model(半兰伯特光照模型)和使用一个ramp texture来控制diffuse shading。 准备工作 同样,我们需要你已经做好了上一篇文章中的内容,并得到了如下shader: Shader “Cu...

weixin_34041003的博客 305

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Unity Shader漫反射光照模型与半兰伯特光照模型

文章目录一、基础光照模型中漫反射部分的计算公式二、逐顶点光照实现逐顶点代码逐顶点效果图三、逐像素光照模型实现逐像素代码逐像素效果图四、半兰伯特光照模型实现广义半兰伯特光照模型公式半兰伯特光照模型代码实现半兰伯特效果图 上一次我们学习了如何实现一个简单的顶点/片元着色器,今天来学习一下漫反射光照模型以及半兰伯特光照模型的实现。 一、基础光照模型中漫反射部分的计算公式 其中n为模型表面法线,l为光源方向 二、逐顶点光照实现 在unity中的操作步骤与上一次相同 逐顶点代码 Shader "Unity Sh

dishuaicc的博客 3783

Unity 的基础光照

Unity 的基础光照

YF云飞的博客 1614
上一篇: unity3d中如何烘培物体
下一篇: 图文详解Unity3D中Material的Tiling和Offset是怎么回事
cbbbc
博客等级 码龄15年 609粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值