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模型,背光面不会出现高光,感觉效果更好。


学习资料:

【Unity Shader】四、高光反射Specular Shader例子的更多相关文章

  1. 【Unity Shader学习笔记】Unity光照基础-高光反射

    1.原理 1.1.Phong模型 计算高光反射需要表面法线.视角方向.光源方向.反射方向等. 在这四个矢量中,我们实际上只需要知道其中3个矢量即可,而第4个矢量(反射方向r)可以通过其他信息计算得到: ...

  2. 【Unity Shader】(三) ------ 光照模型原理及漫反射和高光反射的实现

    [Unity Shader](三) ---------------- 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 ...

  3. Unity shader学习之高光反射光照模型

    高光反射光照模型的公式如下: Cspecular = Clight * mspecular * max(0, dot(v, r))gloss 要计算高光反射需要知道4个参数:入射光线颜色Cspecul ...

  4. Unity Shader (三)Surface Shader机制

    转自:http://blog.csdn.net/candycat1992/article/details/39994049 写在前面 一直以来,Unity Surface Shader背后的机制一直是 ...

  5. 【Unity Shaders】Mobile Shader Adjustment —— 为手机定制Shader

    本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图.这里是本书所需的代码和 ...

  6. 【Unity Shaders】Shader学习资源和Surface Shader概述

    写在前面 写这篇文章的时候,我断断续续学习Unity Shader半年了,其实还是个门外汉.我也能体会很多童鞋那种想要学好Shader却无从下手的感觉.在这个期间,我找到一些学习Shader的教程以及 ...

  7. Phong & BlinnPhong Specular Shader

    [Phong Specular Shader] 如果物体离摄像机很远,或者不需要高精度镜面反射,则Phong模型适用. Phong模型如下: 使用前必须指定使用自定义Phong. [BlinnPhon ...

  8. 关于Unity中的模型描边与Shader切换(专题二)

    模型描边 1: LOL里面的模型描边效果,点击防御塔会有描边的效果,被攻击的时候模型也要描边凸显一下2: 网上可以找到模型描边的Shader,可以直接下载使用,一组第三方的Shader, 帮我们解决了 ...

  9. Unity GeometryShader(从一个线框渲染的例子开始)

    GeometryShader这个概念,已经出现很久了,但由于性能不佳,所以使用的并不多.甚至移动平台根本就不支持.移动平台的硬件更新速度也是越来越快,GS的应用普及应该不会太远.就现阶段而言,GS来做 ...

随机推荐

  1. MFC动态按钮的创建及其消息响应 和 自定义消息

    原文链接: http://www.cnblogs.com/gaohongchen01/p/4046525.html 动态按钮(多个)的创建: 1.在类中声明并定义按钮控件的ID #define IDC ...

  2. ajaxupload.js调用始终进入error回调

    现象:脚本调用成功,文件上传也成功,但是始终进入error的回调函数. 1. ajaxfileupload.js jQuery.extend({ handleError: function( s, x ...

  3. 手把手带你画一个 时尚仪表盘 Android 自己定义View

    拿到美工效果图.咱们程序猿就得画得一模一样. 为了不被老板喷,仅仅能多练啊. 听说你认为前面几篇都so easy,那今天就带你做个相对照较复杂的. 转载请注明出处:http://blog.csdn.n ...

  4. Oracle in和exists效率问题分析

    --------------------------in和exists效率问题------------- 单说in和exsist,in的效率较差.关于EXISTS与IN的区别:EXISTS检查是否有结 ...

  5. U811.1接口EAI系列之二--生成销售出库单调用U8的EAI通用处理方法--PowerBuilder语言

    1.销售系统销售出库,更新U811.1材料库存的EAI的XML生成. 2.主要根据U8配置会生成出库单和同时是否更新库存量,还是更新现存量等等. 3.具体参考代码如下: 作者:王春天 2013-11- ...

  6. MySql(十七):MySql架构设计——高可用设计之思路及方案

    前言: 数据库系统是一个应用系统的核心部分,要想系统整体可用性得到保证,数据库系统就不能出现任何问题.对于一个企业级的系统来说,数据库系统的可用性尤为重要.数据库系统一旦出现问题无法提供服务,所有系统 ...

  7. Quartz 任务调度框架之Hello World

    0x01 什么是Quartz? Quartz是一个完全由java编写的开源作业调度框架. 0x02 实战Quartz Hello World 创建Maven项目,POM 文件配置如下: <pro ...

  8. VS2010如何重置开发环境

    在利用VS进行软件开发的过程中,我们时不时要因为各种原因,对VS的开发环境进行变动,对于很多初次接触VS这样一个十分好用方便的编程工具的人来说,更改编程环境成了一个难题,今天我们就来讲解一下,如何更改 ...

  9. AI 技术咖们说,进入未来世界首先需要一个“虚拟的我” | 科技生活节倒计时8天

    http://www.tmtpost.com/3367762.html 未来世界是什么样子? 斯皮尔伯格在<头号玩家>中构建了一个充满科技感.富有浓烈浪漫主义色彩的虚拟世界.戴上VR眼镜, ...

  10. GDB 调试解析

    GDB(GNU Debugger)是一个强大的命令行调试工具.大家知道命令行的强大就是在于,其可以形成执行序 列,形成脚本.UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优 ...