Unity3D Shader 法线与基础光照
Unity3D Shader 法线与基础光照前言前面的文章里我们已经反复提到“法线”这个词。比如顶点法线外扩做描边光照方向和法线方向做点乘法线贴图会改变表面受光表现那法线到底是什么为什么一旦涉及光照几乎就一定要用到法线这一篇我们从最基础的角度出发讲清楚法线是什么光照为什么和法线有关dot点乘在光照里代表什么如何写一个最基础的 Lambert 漫反射 Shader什么是法线Normal法线可以简单理解为一个表面“朝向哪里”的方向。它通常是一个单位向量。例如一个朝上的平面它的法线可能是(0,1,0)也就是说这个面朝向 Y 轴正方向。为什么法线和光照有关因为光照最基本的问题就是光线打到这个表面时它到底是正面迎光还是斜着迎光还是背光而判断这件事最直接的方法就是看表面的法线方向看光照方向这两个方向越一致说明表面越正对光源通常就越亮。越偏离通常就越暗。dot 点乘为什么这么重要在 Shader 光照中最常见的一行代码就是dot(normal,lightDir)点乘可以理解成两个方向有多接近。它的结果范围大致是1完全同方向0互相垂直-1完全相反在光照里通常会写成saturate(dot(normal,lightDir))意思是小于 0 的部分直接压成 0大于 1 的部分压成 1这样就得到一个 0~1 的受光强度。最基础的漫反射思想最经典的基础光照模型之一就是Lambert 漫反射。它的核心就是diffusemax(0,dot(N,L))其中N法线方向NormalL光照方向Light Direction当表面正对光源时dot接近 1物体更亮当表面转到侧面时dot接近 0物体更暗这就是最基础的“明暗变化”。在 Shader 中如何拿到法线Mesh 顶点输入里通常这样声明float3 normal:NORMAL;例如structappdata{float4 vertex:POSITION;float3 normal:NORMAL;};这表示从模型数据中读取法线。但要注意这里的法线通常是对象空间法线。而做光照时我们通常更常用世界空间法线。因此往往要转换一下float3 worldNormalUnityObjectToWorldNormal(v.normal);为什么光照计算常用世界空间因为灯光方向通常在世界空间下更容易理解摄像机位置是世界空间物体在场景中的朝向变化也更方便统一计算只要参与同一次运算的数据都在同一个空间结果才是可靠的。一个最基础的 Lambert 光照 Shader下面写一个最基础的版本支持主方向光包含环境光 漫反射使用法线和光照方向做点乘ShaderCustom/SimpleLambert{Properties{_Color(Main Color,Color)(1,1,1,1)}SubShader{Pass{Tags{LightModeForwardBase}CGPROGRAM#pragmavertex vert#pragmafragment frag#pragmamulti_compile_fwdbase#includeUnityCG.cginc#includeLighting.cgincstructappdata{float4 vertex:POSITION;float3 normal:NORMAL;};structv2f{float4 pos:SV_POSITION;float3 worldNormal:TEXCOORD0;};fixed4 _Color;v2fvert(appdata v){v2f o;o.posUnityObjectToClipPos(v.vertex);o.worldNormalUnityObjectToWorldNormal(v.normal);returno;}fixed4frag(v2f i):SV_Target{fixed3 Nnormalize(i.worldNormal);fixed3 Lnormalize(_WorldSpaceLightPos0.xyz);fixed3 ambientUNITY_LIGHTMODEL_AMBIENT.xyz*_Color.rgb;fixed ndotlsaturate(dot(N,L));fixed3 diffuse_LightColor0.rgb*_Color.rgb*ndotl;returnfixed4(ambientdiffuse,_Color.a);}ENDCG}}}代码拆解1 从模型里读取法线float3 normal:NORMAL;2 把法线转到世界空间o.worldNormalUnityObjectToWorldNormal(v.normal);3 获取主光方向fixed3 Lnormalize(_WorldSpaceLightPos0.xyz);这里我们主要讨论方向光Directional Light的基础情况。4 计算漫反射强度fixed ndotlsaturate(dot(N,L));这一步就是核心法线越朝向光源 → 值越大法线越背离光源 → 值越小5 乘上灯光颜色和材质颜色fixed3 diffuse_LightColor0.rgb*_Color.rgb*ndotl;6 加上一点环境光fixed3 ambientUNITY_LIGHTMODEL_AMBIENT.xyz*_Color.rgb;如果没有环境光背光面会黑得太死。效果会是什么样这个 Shader 的效果特点是朝向光源的一面更亮背向光源的一面更暗比纯色 Unlit 更有体积感这就是最基础的“受光”感觉。Half-Lambert让阴影面别那么黑标准 Lambert 在某些卡通或风格化场景里会显得阴影过硬。一个常见改法是floathalfLambertdot(N,L)*0.50.5;再做一次限制halfLambertsaturate(halfLambert);这样背光面不会一下掉到纯黑会显得更“柔和”。很多早期卡通渲染教程都会用这种方法。法线方向不对会发生什么如果法线错误最直接的表现就是明暗反了某些面不该亮却很亮旋转模型后受光异常所以当你发现光照不对时除了看代码也要怀疑模型法线是否正确空间是否统一有没有忘记normalize为什么几乎总要normalize因为法线和光照方向参与点乘时我们通常希望比较的是“方向”而不是长度。所以会习惯这样写fixed3 Nnormalize(i.worldNormal);fixed3 Lnormalize(_WorldSpaceLightPos0.xyz);不归一化虽然有时“看起来也能跑”但结果可能会失真。常见坑1 法线和光照方向不在同一个空间例如法线在对象空间光照方向在世界空间直接点乘就是错的。2 忘了归一化点乘结果会不稳定。3 以为法线就是顶点位置方向法线是“表面朝向”不是顶点坐标。4 背光面全黑以为 Shader 写错了很多时候只是因为你只写了 Lambert没加环境光。总结这一篇最重要的核心只有一句基础光照本质上就是“表面朝向”和“光照方向”的关系。在 Shader 中最关键的公式就是saturate(dot(N,L))你可以先记住下面这几个点NORMAL用来接收模型法线光照计算常用世界空间法线dot(N, L)表示受光强度Lambert是最基础的漫反射模型环境光可以避免背光面死黑后面你继续学Blinn-Phong 高光Fresnel法线贴图PBR其实都是在这个基础上不断往上加东西。

相关新闻

MyBatis-Plus核心功能与生产实践详解

MyBatis-Plus核心功能与生产实践详解

1. MyBatis-Plus核心功能全景解析作为国内Java开发者最常用的ORM框架之一,MyBatis-Plus在传统MyBatis基础上进行了全方位增强。我在实际项目中使用该框架已有五年时间,今天系统梳理其最核心的六大功能模块,结合生产环境中的真实案例&#xff…

2026/10/26 9:33:00 阅读更多 →
《孢子》圆球体角色通关:游戏机制突破与模组开发实战

《孢子》圆球体角色通关:游戏机制突破与模组开发实战

在游戏开发与设计领域,创意玩法探索一直是开发者与玩家共同热衷的话题。《孢子》(Spore)作为一款经典的生命模拟游戏,其开放式的进化机制为无数玩家提供了独特的体验空间。近期,社区中涌现出关于“圆球体”角色通关的讨…

2026/10/24 13:52:23 阅读更多 →
Nacos微服务实战:从服务发现到配置管理的完整指南

Nacos微服务实战:从服务发现到配置管理的完整指南

1. 先搞清楚 Nacos 到底解决微服务架构中的哪些实际问题如果你正在接触微服务项目,或者准备把单体应用拆成微服务,Nacos 这个名字大概率会反复出现。它不是什么高深莫测的理论概念,而是一个实实在在能帮你解决服务发现、配置管理、服务健康监…

2026/10/25 16:43:26 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 12:00:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →