【Unity Shader】四、高光反射Specular Shader例子
http://www.cnblogs.com/guxin/p/unity-diffuse-shader-demo.html
在上文中已经学习了漫反射Diffuse Shader和环境光,现在再在此基础上加入高光反射Specular Shader。
高光反射的计算公式:
高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
当然中间的max(0, cos(反射光方向和视野方向的夹角)部分也可以改用半兰伯特模型以增强背光面的光照效果。
逐顶点高光反射
Shader "Custom/07-Specular Vertex" { // 逐顶点高光反射
Properties{
_Diffuse("Diffuse Color", Color) = (,,,) // 可在编辑器面板定义材质自身色彩
_Gloss("Gloss", Range(,)) = // 高光的参数
}
SubShader{
Pass {
// 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
// 包含unity的内置的文件,才可以使用Unity内置的一些变量
#include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse; // 使用属性
half _Gloss;
struct a2v
{
float4 vertex : POSITION; // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
float3 normal : NORMAL; // 告诉Unity把模型空间下的法线方向填充给normal属性
};
struct v2f
{
float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标
float3 color : COLOR; // 用于传递计算出来的漫反射颜色
};
// 计算顶点坐标从模型坐标系转换到裁剪面坐标系
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// 法线方向。把法线方向从模型空间转换到世界空间
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); // 反过来相乘就是从模型到世界,否则是从世界到模型
// 光照方向。
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
/*
* 漫反射Diffuse = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
*/
fixed3 diffuse = _LightColor0.rgb * max(, dot(normalDir, lightDir)) * _Diffuse.rgb; // 颜色融合用乘法
// 反射光的方向
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir)); // 参数:平行光的入射方向,法线方向。而lightDir光照方向是从模型表面到光源的,所以取负数。
// 视野方向 = 摄像机的位置 - 当前点的位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz);
/*
* 高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
*/
fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir, viewDir), ), _Gloss);
// 最终颜色 = 漫反射 + 环境光 + 高光反射
f.color = diffuse + ambient + specular; // 颜色叠加用加法(亮度通常会增加)
return f;
}
// 计算每个像素点的颜色值
fixed4 frag(v2f f) : SV_Target
{
return fixed4(f.color, ); // f.color是float3已经包含了三个数值
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素高光反射
Shader "Custom/08-Specular Fragment" { // 逐像素高光反射
Properties{
_Diffuse("Diffuse Color", Color) = (,,,) // 可在编辑器面板定义材质自身色彩
_Gloss("Gloss", Range(,)) = // 高光的参数
}
SubShader{
Pass {
// 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
// 包含unity的内置的文件,才可以使用Unity内置的一些变量
#include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse; // 使用属性
half _Gloss;
struct a2v
{
float4 vertex : POSITION; // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
float3 normal : NORMAL; // 告诉Unity把模型空间下的法线方向填充给normal属性
};
struct v2f
{
float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标
float3 worldNomal : TEXCOORD0;
float3 worldVertex : TEXCOORD1;
};
// 计算顶点坐标从模型坐标系转换到裁剪面坐标系
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间
// 法线方向。把法线方向从模型空间转换到世界空间
f.worldNomal = mul(v.normal, (float3x3)unity_WorldToObject); // 反过来相乘就是从模型到世界,否则是从世界到模型
f.worldVertex = mul(v.vertex, unity_WorldToObject).xyz;
return f;
}
// 计算每个像素点的颜色值
fixed4 frag(v2f f) : SV_Target
{
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// 法线方向。
fixed3 normalDir = normalize(f.worldNomal); // 单位向量
// 光照方向。
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
// 漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
fixed3 diffuse = _LightColor0 * max(, dot(normalDir, lightDir)) * _Diffuse.rgb; // 颜色融合用乘法
// 反射光的方向
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir)); // 参数:平行光的入射方向,法线方向。而lightDir光照方向是从模型表面到光源的,所以取负数。
// 视野方向 = 摄像机的位置 - 当前点的位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldVertex);
/*
* 高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
*/
fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir, viewDir), ), _Gloss);
// 最终颜色 = 漫反射 + 环境光 + 高光反射
fixed3 tempColor = diffuse + ambient + specular; // 颜色叠加用加法(亮度通常会增加)
return fixed4(tempColor, ); // tempColor是float3已经包含了三个数值
}
ENDCG
}
}
FallBack "Diffuse"
}
效果比较
如下图,左边是逐顶点计算,右边是逐像素计算。能明显看出来逐顶点计算的光斑块状明显,效果不如逐像素计算的好。

使用Blinn-Phong光照模型
Blinn光照模型:
Specular = 直射光 * pow( max(cosθ,0),10) θ:是反射光方向和视野方向的夹角
Blinn-Phong光照模型:
Specular = 直射光 * pow( max(cosθ,0),10) θ:是法线和x的夹角 x:是平行光和视野方向的平分线
所以只需修改上面例子中的公式计算部分即可。
// 光照方向和视野方向的夹角的平分线
fixed3 halfDir = normalize(lightDir + viewDir); fixed3 specular = _LightColor0.rgb * pow(max(dot(normalDir, halfDir), ), _Gloss);
改用Blinn-Phong光照模型后能看到高光光斑变得更圆了一些,效果如下图:

下图左边是Blinn-Phong模型,背光面不会出现高光,感觉效果更好。

学习资料:
- 逐顶点计算高光反射 http://www.sikiedu.com/course/37/task/443/show#
- 逐像素计算高光反射 http://www.sikiedu.com/course/37/task/445/show
- 使用Blinn-Phong光照模型 http://www.sikiedu.com/course/37/task/446/show
【Unity Shader】四、高光反射Specular Shader例子的更多相关文章
- 【Unity Shader学习笔记】Unity光照基础-高光反射
1.原理 1.1.Phong模型 计算高光反射需要表面法线.视角方向.光源方向.反射方向等. 在这四个矢量中,我们实际上只需要知道其中3个矢量即可,而第4个矢量(反射方向r)可以通过其他信息计算得到: ...
- 【Unity Shader】(三) ------ 光照模型原理及漫反射和高光反射的实现
[Unity Shader](三) ---------------- 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 ...
- Unity shader学习之高光反射光照模型
高光反射光照模型的公式如下: Cspecular = Clight * mspecular * max(0, dot(v, r))gloss 要计算高光反射需要知道4个参数:入射光线颜色Cspecul ...
- Unity Shader (三)Surface Shader机制
转自:http://blog.csdn.net/candycat1992/article/details/39994049 写在前面 一直以来,Unity Surface Shader背后的机制一直是 ...
- 【Unity Shaders】Mobile Shader Adjustment —— 为手机定制Shader
本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图.这里是本书所需的代码和 ...
- 【Unity Shaders】Shader学习资源和Surface Shader概述
写在前面 写这篇文章的时候,我断断续续学习Unity Shader半年了,其实还是个门外汉.我也能体会很多童鞋那种想要学好Shader却无从下手的感觉.在这个期间,我找到一些学习Shader的教程以及 ...
- Phong & BlinnPhong Specular Shader
[Phong Specular Shader] 如果物体离摄像机很远,或者不需要高精度镜面反射,则Phong模型适用. Phong模型如下: 使用前必须指定使用自定义Phong. [BlinnPhon ...
- 关于Unity中的模型描边与Shader切换(专题二)
模型描边 1: LOL里面的模型描边效果,点击防御塔会有描边的效果,被攻击的时候模型也要描边凸显一下2: 网上可以找到模型描边的Shader,可以直接下载使用,一组第三方的Shader, 帮我们解决了 ...
- Unity GeometryShader(从一个线框渲染的例子开始)
GeometryShader这个概念,已经出现很久了,但由于性能不佳,所以使用的并不多.甚至移动平台根本就不支持.移动平台的硬件更新速度也是越来越快,GS的应用普及应该不会太远.就现阶段而言,GS来做 ...
随机推荐
- JAVA环境变量的脚本
简单的一个脚本,用于自动设置环境变量.把代码拷贝,另存为javaEnv.bat.安装完Java 2 SDK之后,开一个命令行窗口,输入javaEnv java2SDKDir(java2SDKDir是你 ...
- Oracle 12C -- 使用local PDB克隆新的PDB
1.将用于克隆的PDB至于只读状态:startup open read only SQL> select con_id,name,open_mode from v$pdbs; CON_ID NA ...
- 由苹果的低级Bug想到的
2014年2月22日,在这个“这么二”的日子里,苹果公司推送了 iOS 7.0.6(版本号11B651)修复了 SSL 连接验证的一个 bug.官方网页在这里:http://support.apple ...
- 【LeetCode-面试算法经典-Java实现】【059-Spiral Matrix II(螺旋矩阵II)】
[059-Spiral Matrix II(螺旋矩阵II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given an integer n, generate a ...
- appium简明教程(2)——appium的基本概念
Client/Server Architecture appium的核心其实是一个暴露了一系列REST API的server. 这个server的功能其实很简单:监听一个端口,然后接收由client发 ...
- 利用AutoSPSourceBuilder和Autospinstaller自动安装SharePoint Server 2013图解教程——Part 1
这是一篇对之前 <利用AutoSPSourceBuilder和Autospinstaller自动安装SharePoint Server 2013图解教程——Part 2>的补充.本篇博客将 ...
- The password supplied with the username Domain\UserName was not correct. Verify that it was entered correctly and try again
起因 今天想进入SharePoint 2013 Central Administration创建一个WebApplication,尽然发生了错误: The password supplied with ...
- 5.翻译:EF基础系列---EF中的上下文类
原文地址:http://www.entityframeworktutorial.net/basics/context-class-in-entity-framework.aspx EF中的上下文类是一 ...
- C++ error: passing 'const std::map<>]' discards qualifiers或pass-by-reference-to-const-map导致的“discards qualifiers”
产生问题的场景: int func(const map<int, string> &aMap) { string value = amap[0]; } 或者 int Test:: ...
- IDEA快捷键收集
生成set 和get方法 .生产重写方法Alt+Insert 查看类的所有方法alt + 7 去掉多余的引用包alt + ctrl + O ctrl + alt + t 生成try 语句 自动导入包 ...