Unity Shader实现简单的各向异性渲染(采用各向异性形式的GGX分布)
目录
准备工作
BRDF部分
Unity部分
代码
实现的效果
参考
最近刚结束GAMES202的学习,准备慢慢过渡到GAMES103。GAMES103的作业框架为Unity,并没有接触过,因此准备先学一点Unity的使用。刚好101和202都是渲染相关的,因此先学习使用Unity的Shader。
Unity的Shader采用了HLSL,与GLSL不同的地方挺多(写过202的作业,对GLSL印象不怎么好)。最喜欢的地方就是对于材质,它有一个面板,新建一个材质属性,可以自己方便地调节参数,不用在代码里修改。而GLSL想要使用和调节新的参数,还需要在Shader外部作修改。
在101的作业7中,有一个附加项是自己实现Mircofacet模型,但是当时是刚接触PBR渲染不久,仅仅使用了简单的各向同性的微表面。因此这次写一次各向异性的材质来填一填前面的坑,同时适应以下Unity的Shader部分与通常操作。
准备工作
要实现这个效果,需要先确定BRDF部分。
BRDF部分
以下为通常我们所见到的Mircofacet模型的BRDF

BRDF的效果取决于位于分子的三个部分。采用各向异性的形式,上面的三个部分该如何变化呢?
菲涅尔项F,取决于材质本身的属性,不受到表面状态的变化。比如一块铁,无论如何加工其表面,其菲涅尔项都不会发生变化。这里的我的实现采用了Schlick近似的方法来获取菲涅先项。很显然在公式中,菲涅尔项仅和折射率有关,和表面粗糙度无关。

法线分布函数D,这里采用各向异性形式的GGX分布函数。
下图为GGX分布函数的各向同性形式和各向异性形式


其中,t是切线,b是副切线,n是宏观法线,m是微观法线。αx是切线方向粗糙度,αy是副切线方向粗糙度。
几何遮蔽函数G,这里采用了GGX模型对应的Smith遮蔽项。采用了各项异性的形式。
Smith遮蔽项的通常形式。对于具有形状不变性的NDF,可以求出其Λ函数的解析式。因此得到Λ解析式后,代入该式子就可以求出一个方向的几何遮蔽。对于出射光和入射光分别求一次并将结果相乘就是最后的几何遮蔽项。

下面是各向同性的GGX对应的Smith遮蔽的Λ解析式


在各向异性的情况下,α使用如下的α0。具体的推导过程,可以看文章最后的参考。

这里的θ和Φ都是将入射光方向转为球面坐标后的角度。再将其代入推到成向量的形式,可以得到最后的G1式子

Unity部分
通过了上面的分析,对于着色点的着色需要以下步骤
1.获取法线,切线,副切线
2.获取视线方向和光线方向
3.通过视线方向与光线方向,求得半程向量来代替微观法线方向
4.通过上面的数据,求得F,G,D项
5.在贴图上获取着色点的原始色
6.代入渲染方程完成颜色的计算
对于切线的获取,Unity已经帮我们封装好了功能,不需要再通过uv坐标和点坐标来获取切线了。而副切线只需要法向量叉乘以下切线就可以得到。
代码
Shader "Unlit/My test Shader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_RoughnessX ("RoughnessX" , Range (0.0 , 1.0)) = 0.5_RoughnessY ("RoughnessY" , Range (0.0 , 1.0)) = 0.5_IOR ("IOR" , Range (1.0,30.0)) = 1.5}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#define PI 3.14159265358979323846f#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float4 normal : NORMAL;float4 tangent : TANGENT;};struct v2f{float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;float3 worldTangent : TEXCOORD3;float3 worldBiTangent : TEXCOORD4;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _IOR;float _RoughnessX;float _RoughnessY;//菲涅尔项float fresnel(float3 I,float3 N,float IOR1,float IOR2){float R0 = pow( (IOR1-IOR2) / (IOR1 + IOR2) , 2 );float cos_theta = dot(I,N);return R0 + (1-R0)*pow(1-cos_theta,5);}//各向异性的GGX函数,这里直接搬了UE4的代码实现float DistributionGGX_Aniso( float ax, float ay, float NoH, float3 H, float3 X, float3 Y ){float XoH = dot( X, H );float YoH = dot( Y, H );float d = XoH*XoH / (ax*ax) + YoH*YoH / (ay*ay) + NoH*NoH;return 1 / ( PI * ax*ay * d*d );}//G1float smithG_GGX_aniso(float dotVN, float dotVX, float dotVY, float ax, float ay){return 1.0 / (1.0 + sqrt( (pow(dotVX * ax, 2.0) + pow(dotVY * ay, 2.0)) / pow(dotVN, 2.0)));}//G2float GeometrySmith_aniso(float3 N, float3 V, float3 L, float roughnessX,float roughnessY,float3 X,float3 Y){float dotVN = dot(N,V);float dotVX = dot(V,X);float dotVY = dot(V,Y);float dotLN = dot(L,N);float dotLX = dot(L,X);float dotLY = dot(L,Y);float ggx2 = smithG_GGX_aniso(dotVN,dotVX,dotVY,roughnessX,roughnessY);float ggx1 = smithG_GGX_aniso(dotLN,dotLX,dotLY,roughnessX,roughnessY);if(dotVN <0.0 || dotLN <0.0)return 0.0;return ggx1 * ggx2;}v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.worldTangent = normalize(UnityObjectToWorldDir(v.tangent));o.worldBiTangent = normalize(cross(o.worldNormal , o.worldTangent));UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{float3 normal = normalize(i.worldNormal);float3 lightDir = normalize(_WorldSpaceLightPos0);float3 cameraDir = normalize(_WorldSpaceCameraPos - i.worldPos);float3 tangent = normalize(i.worldTangent);float3 bitangent = normalize(i.worldBiTangent);float NdotL = dot(normal,lightDir);float NdotV = dot(normal,cameraDir);float3 H = normalize(lightDir+cameraDir);float F = fresnel(lightDir,normal,1.0,_IOR);float G = GeometrySmith_aniso(normal,cameraDir,lightDir,_RoughnessX,_RoughnessY,tangent,bitangent);float D = DistributionGGX_Aniso(_RoughnessX,_RoughnessY,dot(normal,H),H,tangent,bitangent);float f = F * G * D / (4 * NdotL * NdotV);//还没熟悉Unity的光源,先用这个了float3 lightRadiance = (1.0,1.0,1.0);float3 texColor = tex2D(_MainTex,i.uv);float3 color = f * NdotL * lightRadiance *texColor;if(NdotL<0.0)color = float3(0.0,0.0,0.0);//因为只有单次光线弹射,物体的背面是黑色的,为了美观加了一个环境光float3 ambient = float3(0.1,0.1,0.1) * texColor;color += ambient;return float4(color,1.0);}ENDCG}}
}
实现的效果
使用了Unity默认的球模型,参数如下图所示,这是对球进行上下打磨后产生的高光作用效果


对球左右打磨后的高光作用效果,参数如下图所示


将两个方向的粗糙度设置为一样,重新回到了各向同性的高光效果,参数如下图所示


参考
[1]【PBR系列五】镜面反射BRDF模型(Specular BRDF)及实现效果
[2] PBR 四 法线分布函数
[3] PBR 五 几何遮蔽函数
相关文章:
Unity Shader实现简单的各向异性渲染(采用各向异性形式的GGX分布)
目录 准备工作 BRDF部分 Unity部分 代码 实现的效果 参考 最近刚结束GAMES202的学习,准备慢慢过渡到GAMES103。GAMES103的作业框架为Unity,并没有接触过,因此准备先学一点Unity的使用。刚好101和202都是渲染相关的,因此先学习…...
React开源框架之Refine
React Refine 是一个基于 React 的开源框架,它旨在帮助开发者快速构建企业级后台管理系统(Admin Panel)。Refine 是由 Retax 演变而来,它提供了一套完整的解决方案,用于构建 CRUD(创建、读取、更新、删除&a…...
【iOS】——渲染原理与离屏渲染
图像渲染流水线(图像渲染流程) 图像渲染流程大致分为四个部分: Application 应用处理阶段:得到图元Geometry 几何处理阶段:处理图元Rasterization 光栅化阶段:图元转换为像素Pixel 像素处理阶段࿱…...
详解CSS
目录 CSS 语法 引入方式 选择器 标签选择器 类选择器 ID选择器 通配符选择器 复合选择器 常用CSS color font-size border width和height padding 外边距 CSS CSS(Cascading Style Sheet),层叠样式表, ⽤于控制⻚⾯的样式. CSS 能够对⽹⻚中元素位置…...
Python执行cmd命令
在Python中执行cmd命令,可以使用内置的subprocess模块。以下是一个简单的例子,展示如何执行一个cmd命令并获取输出。 import subprocess# 要执行的cmd命令 cmd "dir"# 使用subprocess.run来执行命令 result subprocess.run(cmd, shellTrue,…...
基于激光雷达的无人机相互避障
本框架是基于激光雷达的无人机群自主避障代码: 其主体框架利用ORCA算法,他是经典的多智能体相互避障算法,此版本只能规避动态障碍物,不能规避环境形成的静态障碍物我们对ORVA算法稍作修改,使其可以分布式部署ÿ…...
Zookeeper基本原理
1.什么是Zookeeper? Zookeeper是一个开源的分布式协调服务器框架,由Apache软件基金会开发,专为分布式系统设计。它主要用于在分布式环境中管理和协调多个节点之间的配置信息、状态数据和元数据。 Zookeeper采用了观察者模式的设计理念,其核心…...
【生日视频制作】西游记孙悟空师徒提笔毛笔书法横幅AE模板修改文字软件生成器教程特效素材【AE模板】
生日视频制作教程西游记孙悟空师徒提笔毛笔书法横幅AE模板修改文字特效广告生成神器素材祝福玩法AE模板工程 怎么如何做的【生日视频制作】西游记孙悟空师徒提笔毛笔书法横幅AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤: 下载AE模板 安装AE…...
春日美食汇:基于SpringBoot的订餐平台
2 系统关键技术 2.1JSP技术 JSP(Java脚本页面)是Sun和许多参与建立的公司所提倡的动态web技术。将Java程序添加到传统的web页面HTML文件()。htm,。Html) [1]。 JSP这种能够独立使用的编程语言可以嵌入在html语言里面运行,正因为JSP参照了许多编程语言的特性…...
微信小程序中如何监听元素进入目标元素
Page({onLoad: function(){// 如果目标节点(用选择器 .target-class 指定)进入显示区域以下 100px 时,就会触发回调函数。wx.createIntersectionObserver().relativeToViewport({bottom: 100}).observe(.target-class, (res) > {res.inter…...
华为 HCIP-Datacom H12-821 题库 (6)
有需要题库的可以看主页置顶 V群仅进行学习交流 1.转发表中 FLAG 字段中B 的含义是? A、可用路由 B、静态路由 C、黑洞路由 D、网关路由 答案:C 解析: 可用路由用U 表示,静态路由用 S 表示,黑洞路由用 B 表示&#x…...
常见的pytest二次开发功能
pytest框架的二次开发主要是为了满足特定的测试需求或扩展其功能。以下是一些常见的pytest二次开发的功能及其实例,以及如何进行开发的大致步骤: 常见的pytest二次开发功能 定制化测试报告: 功能描述:pytest默认生成的测试报告可…...
Linux下安装MySQL8.0
一、安装 1.下载安装包 先创建一个mysql目录,在将压缩包下载到此 # 下载tar包 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz等待下载成功 2.解压mysql8.0安装包 tar xvJf mysql-8.0.20-linux-glibc2.12-x86…...
【Python】CSV文件的简单使用
1.读取CSV文件 import csvpath "123.csv"with open(path) as f: # 打开csv文件csvReader csv.reader(f) # 读文件建立Reader对象listReader list(csvReader) # 将数据转换成列表print(listReader)2.写入CSV文件 import csvpath "123.csv"with ope…...
jobs命令
jobs命令是Unix/Linux shell中的一个内置命令,用于显示当前shell中正在运行的作业(job)的状态。作业是指在后台执行的命令或命令序列。 jobs命令的基本用法 显示所有作业的状态 当你在终端中启动一个命令并在其后加上&符号时ÿ…...
《深入浅出WPF》读书笔记.11Template机制(上)
《深入浅出WPF》读书笔记.11Template机制(上) 背景 模板机制用于实现控件数据算法的内容与外观的解耦。 《深入浅出WPF》读书笔记.11Template机制(上) 模板机制 模板分类 数据外衣DataTemplate 常用场景 事件驱动和数据驱动的区别 示例代码 使用DataTemplate实现数据样式…...
C语言程序设计(算法的概念及其表示)
一、算法的概念 一个程序应包括两个方面的内容: 对数据的描述:数据结构 对操作的描述:算法 著名计算机科学家沃思提出一个公式: 数据结构 +算法 =程序 完整的程序设计应该是: 数据结构+算法+程序设计方法+语言工具 广义地说,为解决一个问题而采取的方法和步骤…...
【最新华为OD机试E卷-支持在线评测】猜数字(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)
🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…...
上海亚商投顾:深成指、创业板指均涨超1%,华为产业链反复活跃
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 大小指数昨日走势分化,沪指全天震荡调整,2800点失而复得,深成指、创业板指…...
【H2O2|全栈】Markdown | Md 笔记到底如何使用?【前端 · HTML前置知识】
Markdown的一些杂谈 目录 Markdown的一些杂谈 前言 准备工作 认识.Md文件 为什么使用Md? 怎么使用Md? 编辑 怎么看别人给我的Md文件? Md文件命令 切换模式 粗体、倾斜、下划线、删除线和荧光标记 分级标题 水平线 引用 无序…...
从Unity/UE转战Godot 4.2:一个老司机的界面与工作流迁移实战笔记
从Unity/UE转战Godot 4.2:一个老司机的界面与工作流迁移实战笔记当你在Unity或Unreal Engine中已经能闭着眼睛完成场景搭建时,突然面对Godot那个极简的启动界面,可能会产生一种"工具箱被清空"的焦虑。作为同时深度使用过三大引擎的…...
机器学习势能面构建实战:从量子化学数据到高精度分子模拟
1. 项目概述:当机器学习“学会”了化学反应的势能面在计算化学的世界里,我们一直面临着一个核心矛盾:精度与效率的权衡。如果你想精确地描述一个化学反应,比如DNA复制过程中碱基对的质子转移,你需要动用量子化学方法&a…...
MCP插件下载403故障排查:OAuth 2026白名单机制详解
1. 问题现场还原:为什么MCP插件下载页面总卡在403 Forbidden?你点开MCP(Model Control Platform)官方插件市场,选中一个标注“支持v2.8”的调试工具,点击“下载ZIP”,浏览器控制台立刻弹出Faile…...
用Python解放你的记忆:Genanki自动化Anki卡片生成终极指南
用Python解放你的记忆:Genanki自动化Anki卡片生成终极指南 【免费下载链接】genanki A Python 3 library for generating Anki decks 项目地址: https://gitcode.com/gh_mirrors/ge/genanki 你是否曾为手动创建数百张Anki卡片而头痛?是否想过将学…...
Hindsight与金融AI集成:交易决策记忆和分析的终极指南
Hindsight与金融AI集成:交易决策记忆和分析的终极指南 【免费下载链接】hindsight Hindsight: Agent Memory That Learns 项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight Hindsight是一个革命性的智能体记忆系统,专门设计用…...
Bi-LSTM vs CNN-BiLSTM:实战对比哪个模型更适合你的时间序列预测任务?
Bi-LSTM与CNN-BiLSTM实战抉择:时间序列预测的黄金选择法则当面对时间序列预测任务时,选择正确的模型架构往往能决定项目的成败。Bi-LSTM和CNN-BiLSTM作为两种主流的深度学习模型,各自在特定场景下展现出独特优势。本文将带您深入剖析这两种模…...
告别打包焦虑:UE5 Windows与安卓打包速度优化与稳定性提升全攻略
告别打包焦虑:UE5 Windows与安卓打包速度优化与稳定性提升全攻略在虚幻引擎5(UE5)开发流程中,打包环节往往是开发者体验的分水岭——顺畅的打包过程能保持创作心流,而频繁的报错和漫长等待则会严重消耗开发热情。本文将…...
2026年AI模型接口中转站真实测评:五大主流大模型API聚合平台深度实测调研指南
进入2026年,大语言模型的工程化落地已经走完从尝鲜到规模化普及的全流程,对于广大AI应用开发者而言,AI大模型接口中转站早就不是过去仅承担接口转发的简单工具,如今它已经承担起链路高可用保障、多模型负载均衡、跨协议自动转换等…...
SpringBoot+Vue汽车4S店销售管理系统源码+论文
代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 分享万套开题报告任务书答辩PPT模板 作者完整代码目录供你选择: 《SpringBoot网站项目》1800套 《SSM网站项目》1500套 《小程序项目》1600套 《APP项目》1500套 《Python网站项目》…...
保姆级教程:用向日葵远程在Windows系统上安装Vector CANape 21.0.10(附Demo工程位置详解)
Vector CANape 21.0.10安装全流程与实战技巧作为一名长期从事汽车电子测试的工程师,我深知Vector CANape在ECU标定与诊断领域的重要性。本文将从一个技术实践者的角度,详细解析如何高效完成CANape 21.0.10的安装部署,特别是针对远程协作安装场…...
