反射效果
参考文章 Unity Shader-反射效果 Optimized pixel-projected reflections for planar reflectors
反射效果是游戏中常用到的效果,本文主要叙述了四种实现镜面反射的方法,并比对了其中区别
增加相机实现反射最简单最常用的方法就是通过增加一个相机,这个相机和原相机是相对屏幕对称的
SSR(全屏)
SSR(地面)
SSPR
对比总结
顶点法线切线UV和三角面
参考文献:关于顶点的法线、切线、副切线 切线空间与UV坐标 为什么要有切线空间(Tangent Space)
我们知道Mesh(网格)决定了物体的形体结构,其中包含的数据也是渲染物体的基本数据来源,Mesh包含的数据主要是顶点数据和图元数据,这边的图元又可以是点、线、可三角面,或是是其他多边形,在Unity中图元一般为三角面。顶点数据包含了顶点位置、UV、顶点法线、切线等信息,三角面包含了构成三角面的顶点数据。
如下图所示,一个正方形有6个面,8个顶点,但是它有24份顶点数据,12个三角面。
那么为什么不是8份顶点数据呢,因为如果只有8份顶点数据,也就是每个顶点一个数据,那么一个顶点就只有一个法线,一个顶点法线被三个面共享的话。可以明显看到,正方形的顶点上的三个面的光照表现是截然不同的,也就是需要法线的突变,而一个顶点法线被三个面共享的话,这样三个面的法线数据会被插值,就会使一种平滑的光照表现,如下图,显然这种光照表现是不对的,因此至少需要24份顶点法线数据。此外,对于一个一个正方形的面,要自定义它的UV,至少需要4个顶点UV ...
屏幕后处理(四)
本文主要参考Unity Shader-Ambient Occlusion环境光遮蔽
SSAOSSAO是通过屏幕空间计算获得的环境光遮蔽,对于最基本的SSAO其主要思路是在像素点的法线方向的半球中随机撒一些采样点,通过深度图来判断这些采样点是否被物体遮挡,被遮挡的点的百分比越大,则AO值越大
核心代码如下,具体实现细节已注释
v2f vert_ao(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float4 clipPos = float4(v.uv * 2 - 1.0, 1.0, 1.0);
float4 viewRay = mul(_InverseProjectionMatrix, clipPos);
//获得视空间下和该顶点同方向的远裁面坐标
o.viewRay = viewRay.xyz / viewRay.w;
return o;
}
fixed4 frag_ao(v2f i) : SV ...
ComputeShader
基本原理ComputeShader是专门用于大量并行计算的技术,在图形渲染中经常需要用到,其主要原理是通过多线程将大量并行计算从CPU转移到GPU中,既能加速计算也能节约GPU资源,在屏幕后处理中的自动曝光效果就用到了该技术来快速计算每帧的屏幕平均亮度
其线程结构如下图:
其中Compute.Dispath的三个参数分别为线程组的x,y,z, (5,3,2)表示一共有5*3*2个线程组,而每个线程组的大小由numthreads的x,y,z决定,其最大数量为1024(SM5.0).也就是说最多有1024个线程进行并行计算
其中调用ComputeShader的主函数时有四个重要参数
SV_GroupThreadID :是指该线程在该线程组的三维坐标,如图中在(7,5,0)的线程的SV_GroupThreadID就是(7,5,0)
SV_GroupIndex :是指该线程在该线程组的索引位置,如图中在(7,5,0)的线程的索引坐标就是 0*10(numthreads.x) *8(numthreads.x) + 5*10 + 7 = 57
SV_GroupID:是指该线程所在线程组 ...
屏幕后处理(三)
Auto Exposure 自动曝光是指模拟人眼对于光线的调节和感知。人从暗处到亮处,瞳孔会逐渐缩小,视觉会有突然变亮然后逐渐变暗的感受,相反的,从亮处到暗处,瞳孔会逐渐变大,视觉会有突然变暗然后逐渐变暗的感受。为了模拟这一现象,需要通过曝光值来调整整个屏幕的亮暗,这个曝光值由当前屏幕的亮暗程度决定。要获取当前屏幕的亮暗情况的准确值,需要遍历屏幕的每个像素,获取每个像素的亮度取平均来获得平均亮度值,但是对于一个1920 * 1080分辨率的屏幕来说,每帧都需要进行1920 * 1080次GetPixel,如此庞大的CPU计算量显然会影响到帧率,即使是进行降采样获取近似亮度值也是治标不治本,因此需要将这部分计算转移到ComputeShader中,以下以Unity中预设的Auto Exposure功能来说明实现方案。
首先说明下各个输入参数的含义,后续说明都会使用到
参数名
含义
Filtering
过滤区间
Minimun(EV)
最小平均亮度(对数形式)
Maximun(EV)
最大平均亮度(对数形式)
Exposure Compensatio ...
屏幕后处理(二)
高斯模糊模糊算法是很多屏幕后处理效果的基础,如Bloom,景深,镜头渲染,其中效果比较好的是高斯模糊,高斯模糊是一种带权重的模糊算法,越中心权重越高。对于大小为K的卷积核,不进行优化的复杂度为K x K x M x N, 利用高斯模糊线性可分的特性,可以将过程优化为一次水平和一次垂直方向的模糊,让复杂度变为2 x K x M x N
此外,我们还可以通过降采样的方式来降低算法复杂度,以及调整模糊次数来调节模糊效果
相关代码如下:
//定义高斯模糊的两种采样
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748v2f vertBlurVertical(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); half2 uv = v.uv; o.uv[0] = uv; o.uv[1] = uv + float2(0.0, _Mai ...
屏幕后处理(一)
基本调色
亮度:表示颜色的明暗程度,亮度的公式为 :luminance = 0.2125 * col.r + 0.7154 * col.g + 0.0721 * col.b,调节亮度是通过颜色的RGB直接与亮度系数相乘
饱和度:彩色偏离同亮度灰色的程度,饱和度大,偏离越大,色彩越鲜艳,可以通过lerp同亮度的灰色和当前颜色来调整
对比度:指的是一幅图像中明暗区域最亮的白和最暗的黑之间的差距,对比度越大,差距越大,可以通过lerp(0.05,0.5,0.5)的灰色和当前颜色来调整
fixed4 renderTex = tex2D(_MainTex, i.uv);
//调节亮度
fixed3 finalColor = renderTex.rgb * _Brightness;
//获取亮度值
fixed luminance = luminanceFun(finalColor);
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
//调节饱和度
finalColor = lerp(luminanceColo ...
头发的各向异性高光
概念各项同性材质渲染可以认为是在所有方向上均匀地散射光,可以认为一个点的法线N是固定的,而各项异性材质的渲染,在微观上是有各种颗粒和划痕的,一个表面上的点可能有多个法线,所以我们不能用普通的高光模型去计算各向异性的高光
Kajiya-Kay Model原理依据Blin-Phong 光照模型 高光的强度是通过 pow(max(0, NdotH), _SpecularPow) 计算的,但是各项异性中N没法直接获取,我们需要通过切线T和方向和入射光方向来确定该平面上的法线
在数值上,我们会发现TH的夹角正弦会等于HN的夹角余弦值,得到如下代码
fixed dotTH = dot(tangent, halfDir);
fixed sinTH = sqrt(1.0 - dotTH * dotTH);
//衰减值,dotTH大于0不衰减,dotTH小于0衰减
fixed dirAtten = smoothstep(-1.0, 0.0, dotTH);
fixed kkSpecular = dirAtten * pow(sinTH, _SpecularPowH) * _SpecularAtte ...
卡通风格渲染-初级
卡通风格渲染,或者叫非真实感渲染(NPR),是游戏中常用的渲染方法,本文主要参考Unity入门精要和一些网上资料对这块内容进行初步了解和学习
卡通风格着色相较于传统的渲染方式,卡通风格的渲染的色差会更为明显,在Shader中这一改变主要是通过diffuse的结果进行处理,将NDotL的结果进行离散化并通过Smoothstep将边缘进行柔化
通过控制SmoothStep的阈值和区间大小,我们可以控制光影的过渡效果
此外,我们常会用到一张RampTexture实现漫反射的颜色梯度变化
123fixed halfLambert = 0.5* dot(lightDir, worldNormal) + 0.5;fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb;
卡通风格高光对于卡通风格的高光,我们需要和漫反射进行相似的处理将NDotH进行离散化处理
fixed spec = dot(worldNormal, halfDir);
fixed w = fwidth(s ...