【《Unity Shader入门精要》 提炼总结】(八)第八章·Phong公式介绍&高光反射Shader编写&逐顶点光照&逐像素光照&Blinn-Phong光照

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/iceSony/article/details/84349017

 

 

 

本文由@唐三十胖子出品,转载请注明出处。  
文章链接:https://blog.csdn.net/iceSony/article/details/84349017

 

 

这篇文章将总结和提炼《Unity Shader入门精要》的第六章“Unity中的基础光照”的内容。

通过这篇文章,你可以知道

1)Phong公式介绍

2)高光反射的逐顶点光照模型

3)高光反射的逐像素光照模型

4)高光反射的Blinn-Phong光照模型

一.Phong公式介绍

Phong公式:高光反射颜色=光的颜色*高光反射系数*max(0,反射光在视角方向的投影)^反射度

(ShaderLab编写中,高光反射系数与反射度都是在属性中定义的)

那么反射光与视角方向怎么获取呢?

通过上一章节的学习我们知道NORMAL语义是模型空间法线,_WorldLightPos0是世界空间光照方向

可以通过内置函数reflect(法线,光照方向)

由于获取反射方向由于光线i是指向顶点的所以我们relect最后要加个负号:)

视角方向其实Unity中也有相关获取方式:_WorldSpaceCameraPos.xyz

好了有了这些参数我们开始实现吧。

二.高光反射的逐顶点光照

第一步设置属性

Properties
{
    _Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
    _Gloss("高光区域",Range(8, 256)) = 20
}

第二步设置pass的调用与光照模式(结构体和漫反射光照没什么区别)

Pass
{
    Tags{ "LightMode" = "ForwardBase"}
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #include"Lighting.cginc"
    float4 _Diffuse;
    float4 _Specular;
    float _Gloss;
    struct a2v 
    {
        float4 pos : POSITION;
        float3 normal :NORMAL;
    };
    struct v2f
    {
        float4 pos : SV_POSITION;
	float3 color :COLOR;
    };

重点来了顶点着色器编写

v2f vert(a2v v) 
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.pos);
    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    float3 normal2 = (mul(v.normal, (float3x3)unity_WorldToObject));

    float3 lightDir = (_WorldSpaceLightPos0.xyz);
    float3 reflectDir = -(reflect(lightDir, normal2));
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - o.pos);
    float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir, viewDir)), _Gloss);
			
    o.color = ambient + specular;
    return o;
}

lightDir 获取光照方向、reflectDir 通过法线与光照方向获取反射方向、_WorldSpaceCameraPos代表世界坐标下摄像机位向量,再减去世界坐标下的顶点向量,得到的就是视角方向viewDir

唯一需要注意的是视角方向viewDir必须加获取单位向量,很明显这里的pow代表的是颜色的深度,如果你viewDir不加限制会影响数据的!

最后效果

因为没有添加漫反射的原因,所以物体默认是黑色的

和漫反射类似的问题,高光部分不平滑,原因是逐顶点计算是非线性的。

完整代码如下

Shader"sony/Shader150"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
	SubShader
	{
		Pass
		{
			Tags{ "LightMode" = "ForwardBase"}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include"Lighting.cginc"
			float4 _Diffuse;
			float4 _Specular;
			float _Gloss;
			struct a2v 
			{
				float4 pos : POSITION;
				float3 normal :NORMAL;
			};
			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 color :COLOR;
			};
			v2f vert(a2v v) 
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.pos);
				float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				float3 normal2 = (mul(v.normal, (float3x3)unity_WorldToObject));

				float3 lightDir = (_WorldSpaceLightPos0.xyz);
				float3 reflectDir = -(reflect(lightDir, normal2));
				float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - o.pos);//必须加normalize
				float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);

				o.color = ambient + specular;
				return o;
			}
			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(i.color,1.0);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

三.逐像素光照

先看实现,右边逐像素光照看起来润滑很多

结构体的修改

struct v2f
{
    float4 pos : SV_POSITION;
    float3 texcoord :TEXCOORD0;
    float3 texcoord1 :TEXCOORD1;
};

函数的修改

v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
		float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);//必须加normalize
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}

注意这里的结构体里面删掉了颜色,改变成TEXCOORD0

如果不知道什么是TEXCOORD的点击☞   https://blog.csdn.net/iceSony/article/details/84309811

 

这里其实也可以指定类型为NORMAL

但是如果texcoord:NORMAL ,texcoord1:NORMAL ,这样编辑器会报错

所以为了指定float3类型我们常用TEXTCOORD

完整代码

Shader"sony/Shader152"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
		SubShader
	{
		Pass
	{
		Tags{ "LightMode" = "ForwardBase" }
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
		float4 _Diffuse;
	float4 _Specular;
	float _Gloss;
	struct a2v
	{
		float4 pos : POSITION;
		float3 normal :NORMAL;
	};
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord :NORMAL;
		float3 texcoord1 :TEXCOORD1;
	};
	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
                //必须加normalize
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}
		ENDCG
	}
	}
	FallBack "Diffuse"
}

四.Blinn-Phong光照模型

最后来介绍一种真实开发中使用的高光反射模型

逐顶点光照的改版frag函数,代码修改仅两行

float3 halfDir = normalize(lightDir + viewDir);
float3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(halfDir,reflectDir)), _Gloss);

这种光照模型计算最贴近真实情况

代码如下

Shader"sony/Shader154"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
		SubShader
	{
		Pass
	{
		Tags{ "LightMode" = "ForwardBase" }
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
		float4 _Diffuse;
	float4 _Specular;
	float _Gloss;
	struct a2v
	{
		float4 pos : POSITION;
		float3 normal :NORMAL;
	};
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord :NORMAL;
		float3 texcoord1 :TEXCOORD1;
	};
	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
		float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);//必须加normalize
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}
		ENDCG
	}
	}
		FallBack "Diffuse"
}

 

事实上除了使用mul矩阵计算来转化向量空间,UNITY也提供一些CG函数帮助计算

下一章节我们将介绍Shader编程的纹理相关知识,毕竟好的效果离不开图片的支撑。

Excelsior!

展开阅读全文

关于glsl逐像素光照的问题

02-02

为了实现法线贴图弄的着色器,画模型的时候用的立即模式,法线用glNormal3fv指定了(模型格式里有法线数据),切线用glVertexAttrib3fv指定了(拿的相邻顶点的坐标相减算出来的)。切线和法线都是世界坐标系下的向量。传入的一致变量(lightpos,eyepos)的坐标都是世界坐标系下的。(其实我是想弄方向光,但是不会计算lightdir,只能拿当前模型世界坐标加上一个很远的距离算出lightpos再在shader里面算lightdir)rnrn得到的光照结果非常的奇怪,不知道为什么,法线贴图是抄来的应该没问题rnrn顶点着色器rn[code=c]rnuniform vec3 lightpos;rnuniform vec3 eyepos;rnvarying vec3 lightvec;rnvarying vec3 halfvec;rnattribute vec3 tangent;rnrnvoid main(void)rnrn vec4 pos = gl_ModelViewMatrix * gl_Vertex;rn pos = pos / pos.w;rnrn vec4 vlightpos = (gl_ModelViewMatrix * vec4(lightpos, 1.0));rn vec4 veyepos = (gl_ModelViewMatrix * vec4(eyepos, 1.0));rnrn vec3 lightdir = normalize(vlightpos.xyz - pos.xyz);rn vec3 eyedir = normalize(veyepos.xyz - pos.xyz);rnrn vec3 n = normalize(gl_NormalMatrix * gl_Normal);rn vec3 t = normalize(gl_NormalMatrix * tangent);rn vec3 b = cross(n, t);rn //t = cross(n, b);rnrn vec3 halfdir = normalize(lightdir + eyedir);rnrn lightvec.x = dot(t, lightdir);rn lightvec.y = dot(b, lightdir);rn lightvec.z = dot(n, lightdir);rn lightvec = normalize(lightvec);rnrn vec3 eyevec;rn eyevec.x = dot(t, eyedir);rn eyevec.y = dot(b, eyedir);rn eyevec.z = dot(n, eyedir);rn eyevec = normalize(eyevec);rnrn halfvec = normalize(lightvec + eyevec);rnrn gl_FrontColor = gl_Color;rn gl_TexCoord[0] = gl_MultiTexCoord0;rn gl_Position = ftransform();rnrn[/code]rn片段着色器rn[code=c]rn//fragment shaderrnuniform sampler2D basetex;rnuniform sampler2D normtex;rnuniform vec4 ambient, diffuse, specular;rnuniform float shiness;rnvarying vec3 lightvec;rnvarying vec3 halfvec;rnrnvoid main(void)rnrn vec3 vlightvec = normalize(lightvec);rn vec3 vhalfvec = normalize(halfvec);rn vec4 baseCol = texture2D(basetex, gl_TexCoord[0].xy); rn vec3 tbnnorm = texture2D(normtex, gl_TexCoord[0].xy).xyz;rn rn tbnnorm = normalize(tbnnorm * 2.0 - vec3(1.0));rnrn float diffusefract = max(0.0, dot(vlightvec, tbnnorm));rn float specularfract = max(0.0, dot(vhalfvec, tbnnorm));rn if(specularfract > 0.0)rn rn specularfract = pow(specularfract, shiness);rn rn rn gl_FragColor = gl_Color * vec4(ambient.xyz * baseCol.xyz + rn diffuse.xyz * diffusefract * baseCol.xyz +rn specular.xyz * specularfract, 1.0);rnrn[/code]rnrn固定diffusefract 和specularfract :rn[img=https://img-bbs.csdn.net/upload/201502/02/1422888506_379008.png][/img]rnrn用了法线贴图之后:rn[img=https://img-bbs.csdn.net/upload/201502/02/1422888871_140998.png][/img] 论坛

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