2017-11-05 16:51:54 s10141303 阅读数 705

光照模型

光照模型就是一个公式,使用这个公式来计算在某个点的光照效果。
在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:
* 自发光

类似生活中的萤火虫等自己能够发光

* 高光反射

类似生活中的镜子,近似认为百分百反射出去

* 漫反射

类似生活中的光照射到墙壁上、桌子上的反光不会百分百反射出去,各个方向都会反射。

* 环境光

类似生活中的光照照射在某个物体上,物体漫反射然后反射到其他物体上这些过程的光是环境光。

以上都是模拟生活中的光照效果,并不等同于实际生活中的光照计算,实际生活中的光照会更加复杂。

光照模型中关于漫反射的计算

漫反射计算放在顶点函数里面就是逐顶点光照

Disffuse = 直射光颜色*max(0,cosθ) (θ是法线跟光照的夹角) 如果夹角小于0的话就是背光面就直接取黑色

理论知识:

  • 向量点乘(结果值)

    a*b=|a||b|cosθ

  • 向量叉乘(结果是向量)

    a*b=c c的一个向量,值是|a||b|sinθ

编写漫反射Shader

在Pass块里添加Tags{“LightMode”=”ForwardBase”}和#include “Lighting.cginc” //引用一些写好的程序块 会包含一些获取光照的信息

只有定义了正确的LightMode才能得到一些Unity的内置光照变量。

  • normalize() 将一个向量单位化
  • max()用来取得函数中最大的一个
  • dot()用来取得两个向量的点积
  • _WorldSpaceLightPos0取得平行光的位置
  • _LightColor0取得平行光的颜色
    UNITY_MATRIX_MVP 这个矩阵是将模型空间转换到剪裁空间
  • _World2Object这个矩阵用来将一个方向从时间空间转换到模型空间
  • UNITY_LIGHTMODEL_AMBIENT 用来获取环境光
Shader "AladdinShader/04 Diffuse Tertex Shader"
{
    Properties
    {

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

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息  
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        struct a2v 
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 color:COLOR;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir));//取得第一个直射光的颜色
            f.color = diffuse;
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            return fixed4(f.color,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果:
没有写反射光照之前的效果,红色是均匀的
这里写图片描述
写了光照效果之后,会看到正对光照地方很亮,背对光照的地方就很暗甚至黑色。
这里写图片描述
现在是逐顶点光照,顶点的数量是有限的,如果将计算放在片元函数里面计算效果会更好,但计算量也会更大,那就叫逐片元光照。

给物体自身添加颜色和环境光共同控制

上面物体的颜色只是受环境光的颜色影响,但实际生活中我们看到的物体的颜色是环境光和自身颜色混合的效果。我们在上面的例子的基础上,添加上物体本身的颜色:
给物体添加颜色属性然后*环境光

Shader "AladdinShader/04 Diffuse Tertex Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
    }
    SubShader {
        Pass 
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息  
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;

        struct a2v 
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 color:COLOR;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);

            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            f.color = diffuse + ambient;  //颜色增加 增强
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            return fixed4(f.color,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果:
这里写图片描述
这样直射光设置成白色,颜色本身设置成绿色,并且在面板中调整颜色,物体就显示成这种颜色。颜色融合一般是颜色的改变,颜色相加是强度增强。

环境光设置:Window->Lighting->Ambient Source->Color 一般不建议很强的环境光

逐像素光照

Shader "AladdinShader/05 Diffuse fragment Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
    }
    SubShader {
        Pass 
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息  
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;

        struct a2v 
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 worldNormalDir:COLOR0;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(f.worldNormalDir); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            fixed3 tempColor = diffuse + ambient;  //颜色增加 增强
            return fixed4(tempColor,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果图:
这里写图片描述
一个是逐顶点光照,一个是逐像素光照,自己看还是能看出区别的。逐像素光照会更消耗性能一些,如果不需要很高的精度的话就可以用逐顶点光照代替。

半兰伯特光照模型

模型表面的明亮度直接取决于光线向量(light vector)和表面法线(normal)两个向量将夹角的余弦值。

如果漫反射光强设置为Diffuse,入射光光强为I,光方向和法线夹角为θ,那么兰伯特光照模型可以用下面的公式表示:Diffuse = I * cosθ。
兰伯特光照模型 模型背面全都是黑色都看不清楚
这里写图片描述

为了解决这个问题,改造一些兰伯特光照模型的公式,让背光部分也随着光照强度改变而改变。
半兰伯特光照模型:
为了解决兰伯特模型背光面全都是黑的看不清楚的问题。
Diffuse=直射光颜色 * (cosθ * 0.5 + 0.5)
这里写图片描述

Shader修改

Shader "AladdinShader/06 Diffuse fragment Half Lanbote Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
    }
    SubShader {
        Pass 
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息  
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;

        struct a2v 
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 worldNormalDir:COLOR0;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(f.worldNormalDir); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            float halfLambert = dot(normalDir, lightDir) * 0.5 + 0.5; //半兰伯特光照模型
            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * halfLambert * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            fixed3 tempColor = diffuse + ambient;  //颜色增加 增强
            return fixed4(tempColor,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

这里写图片描述
会明显的感觉模型亮度变亮了,而且背面也不会纯黑了也会渐变,而且正面颜色也会鲜明一些,这样整体效果会更好一些。

2015-04-10 23:53:48 dbtxdxy 阅读数 5124

  现实生活中有许多半透明的情形:玻璃、饮料瓶、塑料袋等等。半透明一个很重要的特点就是可以显示它后面的物体的部分颜色。这个“部分”具体是多少我们常用透明度来表示。

一个简单的透明例子

  想做出透明效果,第一步需要通知Shader我们要使用透明效果了。最简单的方法是在#pragma后面增加一个alpha参数,例如#pragma surface surf Lambert alpha。

  开启透明后,只要对SurfaceOutput的Alpha分量赋值就可以了,范围是[0,1],下面是一个简单的透明Shader:

Shader "Custom/SimpleAlpha" {
    Properties {
        _MainColor ("Main Color", Color) = (1, 1, 1, 1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _Alpha ("Alpha", Range(0, 1)) = 0.5
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert alpha

        fixed4 _MainColor;
        sampler2D _MainTex;
        fixed _Alpha;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            half4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = _MainColor.rgb * c.rgb;
            o.Alpha = _Alpha;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

  调整Alpha值,可以看到透明的效果:

  这里写图片描述

  为了更好地观察透明效果,我们可以在立方体后面添加一个红色的平面。这时候,你可能发现一个奇怪的现象,立方体明明在平面的前面,但是看上去却被平面遮挡了,像这样:

  这里写图片描述

  产生这种情况的原因是没有指定渲染顺序。渲染顺序,顾名思义,就是渲染的前后顺序,也可以理解为优先级。可以通过在Tags中指定键值对来指定Shader的渲染顺序。不过,作为一种全局的顺序机制,Unity已经为我们指定好了一些层级,分别是Background、Geometry、AlphaTest、Transparent、Overlay,分别表示1000, 2000, 2450、3000、4000,顺序越小的越先渲染。默认情况下,Shader的渲染顺序是2000,也就是Geometry,我们可以手动指定渲染顺序:

Tags { "Queue" = "Geometry" "RenderType"="Opaque" }

  这样就代表我们这个Shader的渲染顺序为2000。为了解决上面那个奇怪的显示问题,我们可以把我们的Shader的渲染顺序稍微延后一点:

Tags { "Queue" = "Geometry+1" "RenderType"="Opaque" }

  此时渲染顺序就正确了:

  这里写图片描述

  其实Unity已经给我们定义了专门放置透明材质的层次,我们干嘛不用呢,所以最好的方法是这样写:

Tags { "Queue" = "Transparent" "RenderType"="Opaque" }

使用AlphaTest实现透明效果

  另一种实现透明的方式是利用裁剪。这种方式的基本原理就是利用片段的透明度和我们给定的一个透明度做对比,当片段的透明度大于给定透明度时就表现为不透明,否则就是透明(其实是被丢掉)。因此这种方式作出的效果除了完全透明的部分就是完全不透明的部分。

  要使用AlphaTest,需要在#pragma后面添加alphatest:xxx,这个xxx就是我们给定的透明度。下面是一个简单的AlphaTest的例子:

Shader "Custom/AlphaTest" {
    Properties {
        _MainColor ("Main Color", Color) = (1, 1, 1, 1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _TestVal ("Test Value", Range(0, 1)) = 0.5
    }
    SubShader {
        Tags { "Queue" = "Transparent" "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert alphatest:_TestVal

        fixed4 _MainColor;
        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

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

  为了体现效果我们需要一幅透明度渐变的贴图:

  这里写图片描述

  下面是_AlphaTest在不同值下的效果:

  这里写图片描述 这里写图片描述 这里写图片描述

  这种AlphaTest看上去比半透明的方式更有效率,事实上,大部分情况确实如此。一个例外是在移动设备上,AlphaTest在移动设备上效率非常低,甚至不如第一种半透明的方式。

  本次的工程文件在这里

结束语

  透明材质有非常好的表现效果,可以说在游戏开发过程中是必不可少的,恰当的使用透明也可以极大地节省模型的面数。但是,使用透明会有一定的性能开销,尤其是在移动设备上。可以说,开发移动平台的游戏,资源和效率都是锱铢必较的,开发一款效果又好效率又高的产品真的不容易。OK,下回再见~

2016-10-20 09:12:35 qq_28221881 阅读数 2417

摘自冯乐乐的《unity shader 入门精要》


Shader "Custom/SpecularPixelLevel" {
	Properties
	{
		_Diffuse ("Diffuse" , Color)=(1, 1, 1, 1)
		_Specular ("Specular", Color)=(1, 1, 1, 1)
		_Gloss ("Gloss",Range(8.0 ,256.0))= 20
	}

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

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;

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

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

			v2f vert (a2v v)
			{
				v2f o;

				// Transform the vertex from object space to projection space
				//把局部顶点光照投射到空间
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

				//Transform the normal from object  space to world  space;
				//把局部法线转换到空间法线
				o.worldNormal = mul(v.normal, (float3x3)_World2Object);

				//Transform the vertex from obeject space to world space;
				//把局部顶点光照转换到空间中
				o.worldPos = mul(_Object2World , v.vertex).xyz;

				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				//Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				fixed3 worldNormal = normalize (i.worldNormal);

				fixed3 worldLightDir = normalize (_WorldSpaceLightPos0.xyz);
				
				//Coumpute diffuse term				
				fixed3 diffuse = _LightColor0.rgb * _Diffuse .rgb * saturate(dot(worldNormal,worldLightDir));


				//Get the reflect direction in world space;
				//获取空间中的光照方向
				fixed3 reflectDir = normalize (reflect (-worldLightDir,worldNormal));

				//Get view direction in world space;
				//获得视窗的光照方向
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

				//Compute sepcular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);

				return fixed4(ambient + diffuse + specular, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Specular"
}



Shader "Custom/BlinnPhong" {
	Properties
	{
		_Diffuse ("Diffuse" , Color)=(1, 1, 1, 1)
		_Specular ("Specular", Color)=(1, 1, 1, 1)
		_Gloss ("Gloss",Range(8.0 ,256.0))= 20
	}

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

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;

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

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

			v2f vert (a2v v)
			{
				v2f o;
				// Transform the vertex from object space to projection space
				//把局部顶点光照投射到空间
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

				
				//Transform the normal from object  space to world  space;
				//把局部法线转换到空间法线
				o.worldNormal = mul(v.normal, (float3x3)_World2Object);

				//Transform the vertex from obeject space to world space;
				//把局部顶点光照转换到空间中
				o.worldPos = mul(_Object2World , v.vertex).xyz;

				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				//Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				fixed3 worldNormal = normalize (i.worldNormal);

				fixed3 worldLightDir = normalize (_WorldSpaceLightPos0.xyz);
				

				//Coumpute diffuse term	
				fixed3 diffuse = _LightColor0.rgb * _Diffuse .rgb * saturate(dot(worldNormal,worldLightDir));

				//Get the reflect direction in world space;
				//获取空间中的光照方向
				fixed3 reflectDir = normalize (reflect (-worldLightDir,worldNormal));


				//Get view direction in world space;
				//获得视窗的光照方向
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

				//Get the half direction in world space
				fixed3 halfDir = normalize(worldLightDir + viewDir);

				//Compute sepcular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,halfDir)),_Gloss);


				return fixed4(ambient + diffuse + specular, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Specular"
}





从左至右分别是默认胶囊体,逐像素高光,BlinnPhong和高光模型,效果自行比较。个人人呢还是不错的,因为我把天空盒设置成了黑色,然后默认的就几乎看不到了



2017-01-08 17:21:00 SilverFoxGame 阅读数 114

unity3d 为什么要烘焙?烘焙作用是为了什么?

可以这样理解。你把物体模型放进了场景里之后, 引擎会计算光线,光线照到你的物体的表面形成反光和阴影。 
如果不烘焙, 游戏运行的时候,这些反光和阴影都是由显卡CPU计算出来的。
你烘焙之后,这些反光和阴影都记录到了你的模型里,变成了新的贴图了,运行的时候,显卡CPU不需要进行对环境光效果的运算了。如前面的回答,节约CPU资源。

posted on 2017-01-08 17:21 jiahuafu 阅读(...) 评论(...) 编辑 收藏

2019-11-24 20:44:13 weixin_42487874 阅读数 8

漫反射(Lambert)

  • Lum = C * max(0, cos(L, N))
    • Lum:漫反光亮度。
    • C:漫反光强度(漫反光颜色)。
    • L:入射光方向。
    • N:法线方向。

镜面发射(Phong)

  • Lum = C * pow(max(0, cos(R, V)), gloss)
    • Lum:高光亮度。
    • C:高光强度(高光颜色)。
    • R:反射光方向。
    • V:视角方向。
    • gloss:镜面光滑程度。

半角向量(BlinnPhong)

  • Lum = C * pow(max(0, cos(H, N)), gloss)
    • Lum:高光亮度。
    • C:高光强度(高光颜色)。
    • H:半角向量。
    • N:法线方向。
    • gloss:镜面光滑程度。

Unity 内部的光照函数

  • Forward下的漫反射光照函数:
inline fixed4 LightingLambert(SurfaceOutput s, fixed3 lightDir, fixed atten){
	fixed diff = max(0, dot(s.Normal, lightDir));
	fixed4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
	c.a = s.Alpha;
	return c;
}
  • Forward下的高光函数:
inline fixed4 LightingBlinnPhong(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){
	half3 h = normalize(lightDir + viewDir);
	fixed diff = max(0, dot(s.Normal, lightDir));
	float nh = max(0, dot(s.Normal, h));
	float spec = pow(nh, s.Specular * 128.0) * s.Gloss;

	fixed4 c;
	c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten * 2);
	c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten;
	return c;
}

OpenGL镜面效果

阅读数 2009

没有更多推荐了,返回首页