Unity Shader自定义光照模型

前几天去一家公司面试,因为简历上写着“能写简单的Shader”,就被问了一个问题:怎么自定义一个光照模型?

直接懵逼了,隐隐约约能联想到的什么漫反射光+镜面反射光+环境光...难道是这个?光照模型就是#pragam surface surf Lambert里面的那个Lambert呗?这不都是写好的吗?还能自定义啊?!本着“程序是严谨的”的心态,宁可说不知道,也不能瞎说。。。

今天有空,抓紧学习一下自定义光照模型...


首先,新建一个SurfaceShader,打开后做如下修改

#pragma surface surf Standard fullforwardshadows >> #pragma surface surfMyLightingModel

之后,在SubShader块儿中实现这个光照模型:


[plain]  view plain  copy
  1. inline float4 LightingMyLightingModel(SurfaceOutput s, fixed3 lightDir, fixed atten)  
  2.         {  
  3.             float difLight = max(0, dot (s.Normal, lightDir));  
  4.             float4 col;  
  5.             col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);  
  6.             col.a = s.Alpha;  
  7.             return col;  
  8.         }</span>  

这个的方法名注意,要前面多了一个“Lighting”,如果直接写“MyLightingModel”是会报错滴...因为找不到,这里具体是怎么做的我也不知道,以后知道了再来补充。

其中lightDir是点到光源的单位向量,atten(attenuation)是衰减系数。如果要用到点到摄像机(观察者)的单位向量,就需要另一个参数:half3 viewDir(因为这个模型是个漫反射模型,就先不考虑观察者角度了)。

dot方法是点乘(点积),在这里求的是入射光线和该点法线的相似度,相似度越高,反射的光线就越多。

关于这个_LightColor0变量,查了一下,比较复杂,是根据当前环境中的各种光源计算出来的...我们只管用就好了。

以上就实现了一个简单的反射的光照模型。


最后来一个漫反射+镜面反射的光照模型,是不是传说中的冯氏反射模型呢?暂且就当做是吧...

[plain]  view plain  copy
  1. float4 LightingPhongModel(SurfaceOutput s, float3 lightDir,half3 viewDir, half atten)  
  2.         {  
  3.             float4 col;  
  4.   
  5.             float diffuseF = max(0,dot(s.Normal,lightDir));  
  6.   
  7.             float specF;  
  8.             float3 H = normalize(lightDir+viewDir);  
  9.             float specBase = max(0,dot(s.Normal,H));  
  10.             specF = pow(specBase,8);  
  11.   
  12.             col.rgb = s.Albedo * _LightColor0 * diffuseF *atten + _LightColor0*specF;   
  13.             col.a = s.Alpha;  
  14.             return col;  
  15.         }</span>  

[plain]  view plain  copy
  1. float3 H = normalize(lightDir+viewDir);  
  2. float specBase = max(0,dot(s.Normal,H));  
  3. specF = pow(specBase,8);</span>  
这三句是用来计算镜面反射的,镜面反射必然与观察者的位置有关,所以这个方法里用到了这个viewDir参数。


最后来点理论知识,关于漫反射和镜面反射的计算方式。

漫反射:
漫反射与镜面反射的主要区别是法线的分布。如果物体完全光滑的话,那么法线完全垂直于平面,例如镜面及锃亮的金属表面。而很多看起来很光滑平整的物体,如纸,桌面,衣服等,实际上, 用放大镜仔细观察,就会看到其表面是凹凸不平的。法线并不与肉眼看见的面垂直,而是与实际的面平行。
漫反射:光射到粗糙表面时,表面会把光线向着四面八方反射,所以即使入射线平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射 。这种反射光称为漫反射光。

漫反射的计算
diffuse = Kd x colorLight xmax(N*L,0)
Kd漫反射系数
colorLight 光的颜色
N单位法线向量
L由点指向光源的单位向量
其中N与L点乘,如果结果小于等于0,则漫反射为0

    


镜面反射:
光射到表面光滑平整,法线均匀的物体表面上,这种反射光称为镜面反射光。

【风宇冲】Unity3D教程宝典之Shader篇:第十五讲 <wbr>光照基础
Ks 镜面系数
colorLight 光的颜色
N 单位法线向量
L 由点指向光源的单位向量
V 由点指向观察者的单位向量
H(L+V)向量的单位化向量,即normalize(L+V)
shininess镜面强度系数,值越小,高光越分散;值越高,高光越集中。
facing如果N与L的点乘大于0,则facing为1,如果小于等于0,则facing为0


部分内容参考和引用自风宇冲http://blog.sina.com.cn/s/blog_471132920101dhnv.html


所谓的光照模型(光照方程),就是模型对光线做出的反应。因为材质和表面光滑度的不同,在光线照射到物体表面后,因为对光线的吸收和折射反射等等,最终进入眼睛的光线。

比较经典的一个例子,游戏《半条命》中使用的HalfLambert,修改一下我们的第一个光照模型:

[plain]  view plain  copy
  1. float difLight = max(0, dot (s.Normal, lightDir));  
  2. float hLambert = difLight * 0.5 + 0.5;  
  3. float4 col;  
  4. col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);  
  5. col.a = s.Alpha;  
  6. return col;  

嗯,很简单,想怎么算就怎么算。但是,要做出炫酷的效果,要学的理论知识还有很多很多...
### 使用 Unity Shader Graph 实现自定义光照模型Unity 中,Shader Graph 提供了一种直观的方式来构建和调整着色器效果而不需要编写复杂的 HLSL 或 CG 代码。以下是关于如何使用 Shader Graph 创建自定义光照模型的技术说明。 #### 准备工作 为了开始创建自定义光照模型,在启动项目之前应先熟悉 Shader Graph 的基本操作流程[^1]。这包括但不限于节点连接、属性配置以及材质预览等功能。如果尚未掌握这些基础技能,则建议查阅官方文档或其他入门教程作为补充学习资源。 #### 利用现有案例进行实践 对于希望深入理解并实际动手尝试的人来说,“Shader Graph Custom Lighting Sample Project”是一个非常有价值的参考资料[^2]。此项目不仅提供了完整的源文件下载链接,还附带详细的开发背景介绍和技术解析文章。通过研究该样本中的具体实现方法及其背后的设计理念,能够帮助快速上手复杂场景下的灯光处理逻辑。 #### 渲染模式的选择与影响 当决定采用哪种类型的渲染管线时需考虑目标平台性能需求等因素。通常情况下可以选择两种主要的渲染路径之一——逐顶点或者逐片段(像素)级别上的计算方式来进行表面反射特性的模拟[^3]: - **逐顶点光照 (Per-Vertex Lighting)** : 更加高效但在某些细节表现方面可能有所欠缺; - **逐像素/片段光照 (Per-Fragment/Pixel Lighting)** :虽然消耗更多GPU资源但能提供更高质量的结果; 因此,在设计阶段就需要权衡两者之间的利弊关系,并据此做出合理决策以满足最终视觉呈现的要求。 #### 示例步骤概述 尽管不鼓励直接给出分步指导,但仍可简要描述几个关键环节以便于整体把握整个过程: 1. 打开新建好的Unlit Master Node 并切换至Lit Mode; 2. 添加必要的输入参数比如Normal Map, Albedo Color等等; 3. 构建核心算法部分涉及向量运算及颜色混合等操作; 4. 调整测试直至达到预期效果为止; 下面展示了一个简单的代码片段用于演示如何手动编辑HLSL函数从而引入外部光源方向变量: ```hlsl float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); ``` 请注意上述仅为示意用途的实际应用当中还需要综合考量其他多种因素才能完成全面的功能覆盖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值