Unity Shader实现简单的各向异性渲染(采用各向异性形式的GGX分布)

news2024/9/16 23:11:39

目录

准备工作

        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 100

        Pass
        {
            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 );
            }
            //G1
            float 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)));
            }
            //G2
            float 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 五 几何遮蔽函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116420.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何查看Mac的处理器架构‌‌是ARM还是x86

‌通过命令行查看Mac的处理器架构‌‌ 打开终端&#xff08;Terminal&#xff09;。输入命令 uname -m 并回车。如果输出结果是 arm64&#xff0c;则表示你的Mac使用的是ARM架构&#xff1b;如果输出结果是 x86_64&#xff0c;则表示你的Mac使用的是x86架构。 如图&#xff1…

牛客JZ36 二叉搜索树与双向链表 C++

牛客JZ36 二叉搜索树与双向链表 C 思路&#x1f9d0;&#xff1a; 由图所示&#xff0c;我们看出该链表走的是中序&#xff0c;所以我们可以使用中序遍历的方式来解决这个问题&#xff0c;在遍历过程中&#xff0c;我们创建一个前驱和一个后继结点&#xff0c;来进行链接。 并且…

基于stm32f407的跟随行驶系统项目报告(利用openmv+超声波模块)

2023年全国大学生电子设计竞赛&#xff08;TI杯&#xff09; 2024年05月29日 摘要 本项目的硬件结构&#xff1a;基于STM32F407芯片为主控芯片&#xff0c;由TB6612电机驱动&#xff0c;控制左右轮电机的转动控制小车提供前进前进的速度&#xff0c;通过控制两轮的差数达到稳定…

Hive中的分区表与分桶表详解

目录 分区表和分桶表 分区表 分区表基本语法 1. 创建分区表 2. 分区表读写数据 1&#xff09;写数据 &#xff08;1&#xff09;LOAD &#xff08;2&#xff09;INSERT 2&#xff09;读数据 3. 分区表基本操作 1&#xff09;查看所有分区信息 2&#xff09;增加分区 …

数据库MySQL零基础-下【详细】

目录 六、事务/视图/触发器/存储过程 1、事务的理解 &#xff08;1&#xff09;事务的理解 &#xff08;2&#xff09;事务的特性 2、事务的应用 &#xff08;1&#xff09;事务的开启与提交 # 语法 # 示例 &#xff08;2&#xff09;开启autocommit&#xff08;临时生…

MybatisPlus静态工具 通用枚举

静态工具 有的时候Service之间也会相互调用&#xff0c;为了避免出现循环依赖问题&#xff0c;MybatisPlus提供一个静态工具类&#xff1a;Db&#xff0c;其中的一些静态方法与IService中方法签名基本一致&#xff0c;就在方法例多给出一个参数&#xff0c;操作的实体类类型。…

P3285 [SCOI2014] 方伯伯的OJ

*原题链接* 本题与NOIP2017列队有很多共通之处&#xff0c;都是一开始给我们一个排好编号的队列&#xff0c;然后进行一些操作。 如果n的范围不大&#xff0c;我们会如何做呢&#xff1f;很容易想到权值线段树&#xff0c;以编号为下标建立权值线段树&#xff0c;维护每个下标…

WEB攻防-ASP安全MDB下载植入IIS短文件名写权限解析

知识点&#xff1a; 1、ASP环境搭建组合&#xff1b; 2、ASP-数据库下载&植入&#xff1b; 3、IIS-短文件&解析&写权限&#xff1b; WEB安全攻防 1、web源码&#xff1b; 2、开发语言&#xff1b; 3、中间件平台&#xff1b; 4、数据库类型&#xff1b; 5、…

百度快照劫持之JS劫持诊断与恢复一例

劫持现象&#xff1a; 百度搜索结果中&#xff0c;被劫持网站出现在搜索结果中&#xff0c; 点击进入网站&#xff0c;网站显示正常&#xff0c;数秒后网站自动跳转到彩票网站f51688.com/ff6/。但是第二次点击搜索结果&#xff0c;正常进入网站缺不会跳转到彩票网站。 初步认…

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理&#xff0c;打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名…

GO语言快速入门(比较乱)

一、环境安装 1、安装Go环境 1、官网下载 2、cmd-->go version 3、环境变量 GOROOT&#xff1a;go安装路径 GOPATH&#xff1a;go存放代码的路径 4、GOWorks新建三个文件 5、go env查看配置 2、安装编辑器 GoLand或者VSCode 3、HelloWorld package main //一个程序只有一个…

设计模式学习[5]---装饰模式

文章目录 前言1. 原理阐述2. 举例2.1 人装饰方案一2.2 人装饰方案二2.3 人装饰方案三 总结 前言 近期在给一个已有的功能拓展新功能时&#xff0c;基于原有的设计类图进行讨论。其中涉及到了装饰模式&#xff0c;因为书本很早已经看过一遍&#xff0c;所以谈及到这个名词的时候…

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统&#xff0c;并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址&#xff08;Addressable&#xff09;时&#xff0c;您可以使用该资源的地址从任何地方加载它。无论资源是在…

php转职golang第二期

以下是一份简单的 Go 基本语法笔记&#xff1a; 变量与常量&#xff1a; • var 声明变量。• const 声明常量。数据类型&#xff1a; • 整型、浮点型、布尔型、字符串型等。流程控制&#xff1a; • if-else 语句。• for 循环。函数&#xff1a; • 定义和调用函数。数…

【Hot100】LeetCode—394. 字符串解码

目录 1- 思路栈实现四种情况处理 2- 实现⭐394. 字符串解码——题解思路 3- ACM 实现 原题链接&#xff1a;394. 字符串解码 1- 思路 栈实现四种情况处理 ① 遇到数字&#xff0c;进行倍数相加 、②遇到左括号&#xff0c;压栈之前的元素、③遇到右括号弹出&#xff0c;栈进行…

DFS 算法:洛谷B3625迷宫寻路

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;记忆化搜索DFS 算法&#xf…

理解分类器(linear)为什么可以做语义方向的指导?(解纠缠)

Attribute Manipulation&#xff08;属性编辑&#xff09;、disentanglement&#xff08;解纠缠&#xff09;常用的两种做法&#xff1a;线性探针和PCA_disentanglement和alignment-CSDN博客 在解纠缠的过程中&#xff0c;有一种非常简单的方法来引导G向某个方向进行生成&…

【Android】使用和风天气API获取天气数据吧!(天气预报系列之一)

【Android】使用和风天气API获取天气数据吧&#xff01;&#xff08;天气预报系列之一&#xff09; 古话说得好&#xff0c;要有天气预报&#xff0c;首先需要有天气&#xff0c;和预报。 今天给大家介绍一个好用的天气预报API&#xff1a;和风天气。以及webAPI的使用方法~&a…

【H2O2|全栈】关于HTML(3)HTML基础(二)

HTML相关知识 目录 HTML相关知识 前言 准备工作 标签的具体分类&#xff08;二&#xff09; 本文中的标签在什么位置使用&#xff1f; 本期前置知识点 超文本 超文本引用和源属性 图片标签 锚链接 iframe 锚点 预告和回顾 后话 前言 本系列博客将分享HTML相关…

JavaWeb后端开发总结(3)

AOP基础 AOP概述 首先我们要知道AOP是什么&#xff1f; 看下图 个人解析&#xff1a; AOP叫做面向切面编程&#xff0c;但是实际上就是面向方法编程 图中下面一部分是一个AOP的案例 AOP快速入门案例代码实现 案例&#xff1a;测出业务中各个业务方法所需的执行时间 如果…