介绍

作业1在Unity上的复现, 实现了基于的硬阴影, PCF, PCSS
代码下载: https://github.com/Eagle104fred/Games202_Homework1
Games101 阴影课程笔记:https://smuwm007.feishu.cn/docs/doccnQopokLgMTVbIPsTSxow6He

注意事项

  • 阴影的shader必须写在阴影的接受物体上例如墙壁等地方, 和模型本身的shader没啥关系
  • 注意深度像机的参数设置

实现过程

1.构建shadowMap以及硬阴影

  • 新建unity场景后建立一个Camera, 也可以在下面创建一个光源给模型打光不过不影响阴影效果。
  • 新建一个产生阴影的Object和一个接受阴影的Object

2.设置阴影相机参数

需要增加三个文件, 一个是相机的脚本文件用于传递相机的参数和ShadowMap给Shader, 一个是ShadowMap需要新建一个RenderTexture文件来存储, 一个是用于绘制阴影的空Shader

LightCam.cs

public class LightCam : MonoBehaviour
{// Start is called before the first frame updatepublic Shader shader;Camera mCamera;private void Awake(){mCamera = this.GetComponent<Camera>();mCamera.SetReplacementShader(shader, "");//使用shader进行渲染Shader.SetGlobalTexture("_ShadowMap", mCamera.targetTexture);//拿到shadowMap, 设置为全局供shader使用}// Update is called once per framevoid Update(){Shader.SetGlobalMatrix("_ShadowLauncherMatrix", transform.worldToLocalMatrix);//保存将世界坐标转换到光源坐标的矩阵Shader.SetGlobalVector("_ShadowLauncherParam", new Vector4(mCamera.orthographicSize, mCamera.nearClipPlane, mCamera.farClipPlane));//存储相机内参}
}

ShaderDepth.shader

Shader "Custom/ShaderDepth"
{SubShader{Tags { "RenderType"="Opaque" }Offset 1,1 //绘制深度时候偏移一点位置Pass{}}
}

3.配置接受物体的shader

ShaderRecieve.shader

Shader "Custom/ShadowRecieve"
{Properties{_MainTex ("Texture", 2D) = "white" {}_BaseColor ("BaseColor", Color) = (1,1,1,1)}SubShader{Tags { "RenderType"="Opaque" }Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#pragma enable_d3d11_debug_symbols#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 shadowUV : TEXCOORD0;};struct v2f{float2 uv:TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;float4 shadowPos:TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;sampler2D _ShadowMap;float4x4 _ShadowLauncherMatrix;float3 _ShadowLauncherParam;float4 _BaseColor;//基于shadowmap的硬阴影float HardShadow(v2f i){float4 shadow = tex2Dproj(_ShadowMap,i.shadowPos);//拿到坐标在光源场景下的深度float shadowAlpha = shadow.r;//拿到深度值float2 clipalpha = saturate((0.5-abs(i.shadowPos.xy - 0.5))*20);//限定在0-1之间shadowAlpha *= clipalpha.x * clipalpha.y;float depth = 1-UNITY_SAMPLE_DEPTH(shadow);shadowAlpha*=step(depth,i.shadowPos.z);//如果depth<shadowPos就没有被遮挡return shadowAlpha;}v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);//MVP矩阵float4 worldPos = mul(unity_ObjectToWorld,v.vertex);//模型空间转换到世界空间,相当于进行了model矩阵float4 shadowPos = mul(_ShadowLauncherMatrix,worldPos);//从世界坐标到光源坐标shadowPos.xy = (shadowPos.xy/_ShadowLauncherParam.x+1)/2;//再将-1,1范围转换到0,1范围用于读取shadowMap中的深度shadowPos.z = (shadowPos.z / shadowPos.w - _ShadowLauncherParam.y)  / (_ShadowLauncherParam.z - _ShadowLauncherParam.y);//初始化深度o.shadowPos = shadowPos;o.uv = TRANSFORM_TEX(v.shadowUV, _MainTex);//读取uv//UNITY_TRANSFER_FOG(o,o.vertex);return o;}      float4 frag(v2f i):SV_Target{float4 color = tex2D(_MainTex,i.uv);//拿到主颜色float shadowAlpha=0.0;shadowAlpha = HardShadow(i);  color.rgb *=(1-shadowAlpha)*_BaseColor.rgb;//阴影能见度加上材质本身的颜色return color;}ENDCG}}
}

PCF

泊松盘采样原理
PCF的本质就是每个点的阴影值都取决于附近区域的shadowMap进行采样后,再做卷积的结果,但是如果对周围区域每一个像素都算一遍shaodowMap性能开销很大,因此需要用到随机采样。

泊松盘采样

//采样点个数(泊松盘)
#define NUM_SAMPLES 150
//采样的圈数(泊松盘)
#define NUM_RINGS 10
#define pi 3.141592653589793
#define pi2 6.283185307179586float2 poissonDisk[NUM_SAMPLES];float rand_2to1(float2 uv )
{ // 0 - 1const float a = 12.9898, b = 78.233, c = 43758.5453;float dt = dot( uv.xy, float2( a,b ) ), sn = fmod( dt, pi );return frac(sin(sn) * c);
}
void poissonDiskSamples(const in float2 randomSeed )
{float ANGLE_STEP = pi2 * float( NUM_RINGS ) / float( NUM_SAMPLES );float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );float angle = rand_2to1( randomSeed ) * pi2;//随机初始角float radius = INV_NUM_SAMPLES;float radiusStep = radius;for( int i = 0; i < NUM_SAMPLES; i ++ ){poissonDisk[i] = float2( cos( angle ), sin( angle ) ) * pow( radius, 0.75 );radius += radiusStep;//递增半径angle += ANGLE_STEP;//递增角度}
}

PCF代码

        float PCF(v2f i){float4 shadowCoord = i.shadowPos;poissonDiskSamples(shadowCoord.xy);float textureSize = 2048.0;//shadowMap大小float filterStride = 5.0;float filterRange = filterStride/textureSize;int unBlockCount = 0;float shadowAlpha=0.0;for(int i=0;i<NUM_SAMPLES;i++){float2 sampleCoord = poissonDisk[i]*filterRange+shadowCoord.xy;//泊松偏移乘以采样间隔再加上原点位置float shadow = tex2D(_ShadowMap,sampleCoord);shadowAlpha = shadow.r;float2 clipalpha = saturate((0.5-abs(sampleCoord - 0.5))*20);//限定在0-1之间shadowAlpha *= clipalpha.x * clipalpha.y;//阴影区域裁剪float depth = 1-UNITY_SAMPLE_DEPTH(shadow);if(depth+0.005<shadowCoord.z)//避免自遮挡{unBlockCount++; //计算未被遮挡的采样点数量}}return float(unBlockCount)/float(NUM_SAMPLES)*shadowAlpha;//阴影值为未遮挡概率}

PCSS

PCSS主要分为三部:
1.查找每一个像素的平均深度, 这个过程和pcf很像只不过是统计的是Block而不是unBlock。
2.根据公式就是那个该像素点的Blocker平均深度,计算软阴影的大小。wPenumbra=(dReceiver−dBlocker)∗wLightdBlockerw_{Penumbra}=\frac{(d_{Receiver}-d_{Blocker})*w_{Light}}{d_{Blocker}} wPenumbra​=dBlocker​(dReceiver​−dBlocker​)∗wLight​​
3.正常计算PCF。
findBlocker找到平均深度:

float _findBlocker(float4 shadowPos,float3 normal){float4 shadowCoord = shadowPos;poissonDiskSamples(shadowCoord.xy);float textureSize = 2048.0;//注意 block 的步长要比 PCSS 中的 PCF 步长长一些,这样生成的软阴影会更加柔和float filterStride = 20.0;float filterRange = 1.0 / textureSize * filterStride;int shadowCount = 0;float blockDepth = 0.0;for(int i=0;i<NUM_SAMPLES;i++){float2 sampleCoord = poissonDisk[i]*filterRange+shadowCoord.xy;float shadow = tex2D(_ShadowMap,sampleCoord);float depth = 1-UNITY_SAMPLE_DEPTH(shadow);//计算动态bias(根据光源和法线的夹角动态调整bias)float bias=min(0.009*(abs(1-dot(normal,_ShadowLightDirection))),0.0005);//将dot的0-1取值归一化到0-0.009if(depth+bias<shadowCoord.z){blockDepth+=depth;shadowCount++; //计算未被遮挡的采样点数量}}if(shadowCount==NUM_SAMPLES)return 3.0;return blockDepth/float(shadowCount);}

PCSS:

float PCSS(v2f i){float4 shadowCoord = i.shadowPos;// STEP 1: avgblocker depth    float3 normal = normalize(i.normal);//用于计算动态biasfloat zBlocker = _findBlocker(shadowCoord,normal);//if(zBlocker<EPS)return 0.0;//if(zBlocker>1.0)return 1.0;// STEP 2: penumbra sizefloat W_LIGHT=1.0;float wPenumbra = (shadowCoord.z-zBlocker) * W_LIGHT / zBlocker;// STEP 3: PCFfloat textureSize = 1024.0;float filterStride = 10.0;float filterRange = filterStride/textureSize*wPenumbra;//用距离光源的距离控制软阴影大小int unBlockCount = 0;float shadowAlpha=0.0;for(int i=0;i<NUM_SAMPLES;i++){float2 sampleCoord = poissonDisk[i]*filterRange+shadowCoord.xy;float shadow = tex2D(_ShadowMap,sampleCoord);shadowAlpha = shadow.r;float2 clipalpha = saturate((0.5-abs(sampleCoord - 0.5))*20);//限定在0-1之间shadowAlpha *= clipalpha.x * clipalpha.y;//阴影区域裁剪float depth = 1-UNITY_SAMPLE_DEPTH(shadow);//计算动态bias(根据光源和法线的夹角动态调整bias)float bias=max(0.01*(1-abs(dot(normal,_ShadowLightDirection))),0.005);//将dot的0-1取值归一化到0-0.009if(depth+bias<shadowCoord.z){unBlockCount++; //计算未被遮挡的采样点数量}}return float(unBlockCount)/float(NUM_SAMPLES)*shadowAlpha;}

Games202作业1 Unity(更新完毕)相关推荐

  1. 2017-2018-2 1723《程序设计与数据结构》每周成绩 (更新完毕)

    目录 第 00 周 - 成绩(预备作业03.Linux命令测试) 第 01 周 - 成绩(第一周博客.云班课测试) 第 02 周 - 成绩(第二周博客.云班课测试.课堂表现) 第 03 周 - 成绩( ...

  2. 哪里有c语言教学视频,C语言教学视频(共32课更新完毕)(献给所有的爱好计算机的同学)(更新c+)...

    想学计算机的都知道老鸟们说C语言与汇编,数据结构是计算机中的基础吧 一定要学好学扎实了 想要成为高手这是必经之路 然后各位朋友可能也在学习中遇到了一点困难 我也有过这种迷茫 最近跟着nisy学习 学到 ...

  3. 小鱼儿 c语言,C语言教学视频(共32课更新完毕)(更新c++)

    想学计算机的都知道老鸟们说C语言与汇编,数据结构是计算机中的基础吧 一定要学好学扎实了 想要成为高手这是必经之路 然后各位朋友可能也在学习中遇到了一点困难 我也有过这种迷茫 最近跟着nisy学习 学到 ...

  4. 西卡编程教学 C语言教学视频(共32课更新完毕) - 『 西卡教学 』 - 西卡学院 - Powered by Pureing Labs!...

    西卡编程教学 C语言教学视频(共32课更新完毕) - 『 西卡教学 』 - 西卡学院 - Powered by Pureing Labs! 西卡编程教学 C语言教学视频(共32课更新完毕) - 『 西 ...

  5. 美赛最新通知:论文接收状态已更新完毕!

    今日,美国大学生数学建模竞赛组委会官方微博再次发布关于本次竞赛的通知,原文如下: ️OFFICIAL  ANNOUNCEMENT️ The Solution Status has been updat ...

  6. 《程序员面试金典》解题目录(更新完毕)

    题目来源于LeetCode上的<程序员面试金典>,这里做一个目录方便大家查找.另外有本人的LeetCode解题目录.<剑指Offer>解题目录.LintCode代码能力测试CA ...

  7. 《剑指Offer》解题目录(更新完毕)

    题目来源于LeetCode上的<剑指Offer>,这里做一个目录方便大家查找.另外有本人的LeetCode解题目录.<程序员面试金典>解题目录.LintCode解题目录 可点击 ...

  8. 《北风网网友录制Silverlight入门系列视频教程》共23课时/更新完毕[压缩包]

    中文名: 北风网网友录制Silverlight入门系列视频教程 资源格式: 压缩包 版本: 共23课时/更新完毕 发行日期: 2012年03月05日 地区: 大陆 对白语言: 普通话 文字语言: 简体 ...

  9. Python基础语法视频教程全39集,历时1个多月终于更新完毕,需要的拿走!

    我应该是在4月底5月初的时候,说我准备录制一个python的教学视频. 我坚持了一个多月,每天晚上9点准时更新一集,从未断过. 截止上上周,全部39集python基础语法系列更新完毕. 完整教程目录 ...

最新文章

  1. leetcode 752. 打开转盘锁 c代码
  2. 【线上直播】Xilinx U30 视频转码 + AI 的应用技术实践
  3. C#在Linux上的开发指南
  4. 前端学习(3001):vue+element今日头条管理--项目初始化总结
  5. java project 连接hibernate 出错
  6. mysql 开发进阶篇系列 23 应用层优化与查询缓存
  7. Vue+ElementUI搭建一个后台管理框架
  8. 入侵检测系统原理和实现
  9. 5、ORB-SLAM闭环检测之通过求解出来的sim3寻找当前关键帧和闭环候选帧之间的更多匹配
  10. 电池充电电路(TP4059)详解
  11. 无人机基础知识:多旋翼无人机各模式控制框图
  12. Class 'app\index\controller\News' not found 解决方案
  13. eclipse中找不到jar包中的类与函数java.lang.ClassNotFoundException让jar包全局可用
  14. 番茄钟怎么调_实操番茄钟使用方法
  15. 【SDX12】高通SDX12 NatType功能分析及实现
  16. AB test | 学习笔记
  17. [教你做小游戏] 用86行代码写一个联机五子棋WebSocket后端
  18. 使用matlab读取gml网络数据
  19. 当你学不进去的时候,不防试试“普瑞马法则”
  20. 99%卖家不知道的秘密让您每年节省几万元的“人为干涉订单“费用

热门文章

  1. 【转载】好电影!保存了,没事干的时…
  2. effective morden c++ 2
  3. 2021李宏毅机器学习笔记--12 attack ML models
  4. 简单易懂且有趣的pycharm运行小游戏
  5. 关于搭建yuanshen(yuan神)私人服务器教程
  6. 中英离线翻译mac_Translatium for mac(翻译工具)
  7. 硬汉内贾德:让美国人战栗(推荐)
  8. 加快Kettle插入速度的小技巧
  9. CSP 201609-3 炉石传说(C++)
  10. PTA习题4-11 兔子繁衍问题 (15 分)-好容易入坑