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文件命令 切换模式 粗体、倾斜、下划线、删除线和荧光标记 分级标题 水平线 引用 无序…...

C++17: 用折叠表达式实现一个IsAllTrue函数
前言 让我们实现一个 IsAllTrue 函数,支持变长参数,可传入多个表达式,必须全部计算为true,该函数才返回true。 本文记录了逐步实现与优化该函数的思维链,用到了以下现代C新特性知识,适合对C进阶知识有一定…...

【IPV6从入门到起飞】2-2 获取你的IPV6(Teredo隧道)
【IPV6从入门到起飞】2-2 获取你的IPV6(Teredo隧道) 1 打工人的忧伤2 Teredo介绍2.1 背景2.2 工作原理 3 Linux 服务器获取IPV63.1 安装3.2 设置开机自启动和启动3.3 开放防火墙 UDP 35443.4 查看IPV6以及ping包测试3.5 修改Teredo服务器3.6 重启服务3.7…...

Linux 安全弹出外接磁盘
命令行操作 首先,需要卸载硬盘上的所有分区,可以使用umount来卸载分区 清空系统缓存,将所有的数据写入磁盘 sync 列出已挂载的文件系统 使用lsblk或者df命令来查找要卸载的分区 lsblk or df -h确保没有文件正在使用 使用lsof 命令来…...

面试准备-6
NIO底层是用Selector、Channel和ByteBuffer来实现的。主线程在循环使用select方法进行阻塞等待,当有acceptable(可连接)、readable(可读)或者writable(可写)事件发生的时候,循环就会…...

context canceled 到底谁在作祟?
一、背景 在工作中,因报警治理标准提高,在报警治理的过程中,有一类context cancel报警渐渐凸显出来。 目前context cancel日志报警大致可以分为两类。 context deadline exceeded 耗时长有明确报错原因 context canceled 耗时短无明确报错…...

windows C++ 虚拟内存的按需调拨
虚拟内存的按需调拨 windows C 虚拟内存的按需调拨 文章目录 虚拟内存的按需调拨虚拟内存的按需调拨 虚拟内存的按需调拨 /*------------------------------------------------------------------------24-SEHAndMemory.cpp演示虚拟内存的按需调拨--------------------------…...

[杂项]pugi::xml获取xml中的注释节点
前言 想到学习xml时的一句话,xml中注释也会被算作一个节点。那么我们就可以通过 pugixml 把注释节点获取出来, <?xml version"1.0"?> <mesh name"mesh_root"><!--这是一个注释节点-->some text<
Spring Boot Admin集成与自定义监控告警
目录 一.Spring Boot Admin集成 1.引入依赖 2.添加配置 3.监控界面 二.Spring Boot Admin告警机制 1. 基本告警机制 2. 配置告警 2.1 triggers触发器讲解 3. 自定义通知 3.1 Instance 对象 三.Spring Boot Admin支持的监控属性 1.常见的Spring Boot Admin监控属性 …...

如何恢复回收站中已删除/清空的文件
回收站清空后如何恢复已删除的文件?是否可以恢复永久删除的文件?或者最糟糕的是,如果文件直接被删除怎么办?本文将向您展示清空回收站后恢复已删除数据的最佳方法。 回收站清空后如何恢复已删除的文件? “回收站清空后…...

玩短视频素材都是在哪里找的?推荐几个热门的短视频素材下载渠道
亲爱的短视频创作爱好者们,你是否在寻找视频素材时感到苦恼,觉得选择有限?别担心,今天我要为大家介绍几个超级实用的视频素材下载平台,帮助你的视频创作事半功倍! 蛙学网 我们首先要重点推荐的是蛙学网&am…...