Games202作业1

软阴影(PCF、PCSS) 软阴影与硬阴影的区别如下:上面为硬阴影,下面为软阴影。PCSS ,PCF 阅读详情

Games202课程的作业一,内容是基于ShadowMap实现硬阴影和PCF、PCSS的软阴影。

一、作业框架

作业框架与作业0基本相同,可以参考GAMES202 作业1框架代码结构分析 - 知乎 (zhihu.com)这篇文章,个人觉得非常详细,当然,框架其实并不复杂,花点时间就能搞懂。下面主要说一下需要注意的几点:

  1. 模型加载不出来,与作业0的问题相同,解决方法可参考我的上一篇博客Games202作业0及优化部分-优快云博客
  2. WebGL中的矩阵所采用的是列优先存储,这是其与大多数图形API不同的地方,在做矩阵操作需要注意,否则会得到意想不到的结果
  3. ShadowMap的存储采用的是RGBA四个通道,虽然每个通道都是一个float,但只有一个字节有效,也就是每个通道相当于只有一个字节,每个通道分别存储了深度值(float)的一个字节的精度,所以精度仍然不变,为32bit。将深度值拆解为颜色向量的pack函数位于shadowFragment.glsl中,pack函数采用了一系列位运算操作提取深度值的第 1 2 3 4 个字节并以vec4的形式返回,实现如下:
    // pack函数用于将深度值的浮点数按照字节分别存储在RGBA中
    vec4 pack (float depth) {
        // 使用rgba 4字节共32位来存储z值,1个字节精度为1/256
        const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);
        const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);
        // gl_FragCoord:片元的坐标,fract():返回数值的小数部分
        vec4 rgbaDepth = fract(depth * bitShift);
        rgbaDepth -= rgbaDepth.gbaa * bitMask; // 分别提取第1234个字节的内容, 并存储在RGBA的第一个字节中
        return rgbaDepth;
    }

    相对应的unpack函数位于phongFragment.glsl中,实现如下:

    float unpack(vec4 rgbaDepth) {
        const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));
        return dot(rgbaDepth, bitShift);
    }

    其原理也就不难理解了。

二、ShadowMapping实现

ShadowMap采用经典的two-pass方法实现,即先从光源出发对场景做一次模拟渲染,将得到的深度缓冲保存在阴影贴图中,然后再从相机出发进行渲染,在渲染时比较片元在light space下的深度与阴影贴图中的深度,从而判断该片元是否应该渲染为阴影。对于shadow acne现象,通过添加一个偏置进行缓解。

1.实现光源的MVP矩阵

在DirectionalLight.js中完善CalcLightMVP函数:

CalcLightMVP(translate, scale) {
    let lightMVP = mat4.create();
    let modelMatrix = mat4.create();
    let viewMatrix = mat4.create();
    let projectionMatrix = mat4.create();

    // Model transform
    // webgl中的矩阵是列优先存储,所以矩阵相乘的顺序应该反过来
    // T * R * S * v = S.t * R.t * T.t * v
    mat4.translate(modelMatrix, modelMatrix, translate);
    mat4.scale(modelMatrix, modelMatrix, scale);
    // View transform
    mat4.lookAt(viewMatrix, this.lightPos, this.focalPoint, this.lightUp);
    // Projection transform
    mat4.ortho(projectionMatrix, -150, 150, -150, 150, 0.01, 500);

    mat4.multiply(lightMVP, projectionMatrix, viewMatrix);
    mat4.multiply(lightMVP, lightMVP, modelMatrix);

    return lightMVP;
}

对于投影矩阵使用正交投影,投影边界的参数设置需要通过多次调参得到一个合适的范围。对于MVP变换,一般的顺序是缩放→旋转→平移,但由于WebGL是列优先存储,所以要反过来,原理在注释中很详细了。

2.使用阴影贴图

首先是执行透视除法,并将裁剪空间(-1~1)变换到NDC空间(0~1):

vec3 shadowCoord = vPositionFromLight.xyz / vPositionFromLight.w;
shadowCoord = (shadowCoord + 1.0) / 2.0;

对阴影贴图进行采样,比较深度决定是否可见:

float useShadowMap(sampler2D shadowMap, vec4 shadowCoord){
  float shadowz = unpack(texture2D(shadowMap, shadowCoord.xy));
  if(shadowz + EPS > shadowCoord.z)
    return 1.0;
  else
    return 0.0;
}

可以达到的效果如下,在Edge中测试可以达到90FPS:

可以看到硬阴影效果,但由于ShadowMap分辨率有限,所以出现了很多的锯齿。

这里有些博主会采用自适应的bias来进行优化,但我发现实际效果并不好,比较常见的是这样:

 (图片来自GAMES202 作业1解答_games202作业1_川明_Fragtex的博客-优快云博客)可以发现很多应该出现的阴影消失了,包括模型的辫子、手臂、脚以及模型背后应该出现的阴影全都消失不见,原因就是得到的bias太大(注意模型身上的一些浅阴影其实是贴图中本来就有的)。

而对于直接渲染出来结果,其实可以观察到已经没有shadow acne出现,至于一些“不太自然”的地方,其实是因为阴影贴图的分辨率不够再加上硬阴影所带来的锯齿现象。

三、PCF软阴影

PCF即Percentage Closer Filter,原本是指一种滤波,后来被应用于软阴影技术中。

PCF的主要原理是在对阴影贴图进行采样时,不只采样一个点,而是对UV坐标的周围进行多次采样,将这些采样的平均结果作为一种阴影权重用于混合计算出来的颜色和阴影(也就是黑色),该方法放弃了非0即1的硬阴影做法,可以以较小的代价得到软阴影的效果,但其没有考虑软阴影的程度变化,其使用的PCF核大小是arbitrary的,故而还不能准确模拟现实世界中的阴影效果。

PCF的做法可以分为如下两步:

1.在UV周围得到多个采样点

框架为我们提供了两个二维的随机采样函数:均匀圆盘采样和泊松圆盘采样,二者的区别是均匀圆盘采样在采样方向、采样距离都做了随机操作,而泊松圆盘采样只在一开始选择采样方向时做了一次随机。所以导致的结果就是,均匀圆盘采样可能会出现一些聚集现象,样本之间的分布并不“均匀”,而泊松圆盘采样会呈现十分均匀有规律的采样结果,类似于一个蚊香,逐渐向外伸展开,看上去极度舒适。通过二者的可视化可以很清楚地看到这一点:

均匀圆盘采样
泊松圆盘采样

 2.使用得到的采样点对ShadowMap进行采样,将采样的结果分别与片元的深度值进行比较和平均

float PCF(sampler2D shadowMap, vec4 coords, float filterSize) {
  poissonDiskSamples(coords.xy);

  float res = 0.0;
  float filterRange = filterSize / 2048.0;

  for (int i = 0; i < NUM_SAMPLES; i++) {
    vec2 sampleuv = coords.xy + poissonDisk[i] * filterRange;
    float shadowz = unpack(texture2D(shadowMap, sampleuv));
    if(shadowz + 5.0 * EPS > coords.z)
      res += 1.0;
  }
  return res / float(NUM_SAMPLES);
}

最终可以得到的结果如下:

 采样点为150,滤波大小为7时,测得FPS为30

四、PCSS

PCSS(Percentage Closer Soft Shadow)是对PCF的优化,加入了对滤波核大小的考虑。

在现实世界中,很容易观察到当遮挡物距离阴影越远时,阴影越“软”,原理如下图(来自Games202):

 根据相似三角形原理,很容易得到如下等式:

$w_{\text {Penumbra }}=\left(d_{\text {Receiver }}-d_{\text {Blocker }}\right) \cdot w_{\text {Light }} / d_{\text {Blocker }}$

 w_{\text {Penumbra }}也就阴影从1到0的过渡范围,换言之,在使用PCF时,当w_{\text {Penumbra }}越大,使用的核应该越大。

但在实际中,要想得到公式中的w_{\text {Light}}、d_{\text {Blocker}}等并不容易,可能涉及到射线与物体的求交等十分消耗性能的操作。所以,仍然采用近似的方法,在实现上,对w_{\text {Light}}采用常数,同时,在UV周围进行采样,对采样点从shadowmap中获取深度,从而得到d_{\text {Blocker}}。

首先是查找遮挡物,并求得平均深度,实现如下:

float findBlocker( sampler2D shadowMap,  vec2 uv, float zReceiver ) {
  poissonDiskSamples(uv);

  float res = 0.0;
  float searchSize = 20.0;
  float filterRange = searchSize / 2048.0;
  float blockerDepthSum = 0.0;
  float blockerCount = 0.0;

  for (int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; i++) {
    vec2 sampleuv = uv + poissonDisk[i] * filterRange;
    float shadowz = unpack(texture2D(shadowMap, sampleuv));
    if(shadowz + EPS <= zReceiver) {
      blockerDepthSum += shadowz;
      blockerCount++;
    }
  }
  if(blockerCount == 0.0) {  // 当没有遮挡时,不需要PCF,核大小为0
    return zReceiver;
  }
	return blockerDepthSum / blockerCount;
}

其次是PCSS,计算核大小,然后进行PCF:

#define LIGHTWIDTH 10.0
float PCSS(sampler2D shadowMap, vec4 coords) {

  // STEP 1: avgblocker depth
  float avgblockerDepth = clamp(findBlocker(shadowMap, coords.xy, coords.z), 1e-7, coords.z);
  // STEP 2: penumbra size
  float penumbraWidth = LIGHTWIDTH * (coords.z - avgblockerDepth) / avgblockerDepth;
  // STEP 3: filtering
  float filterSize = 2.0 * penumbraWidth;
  return PCF(shadowMap, coords, filterSize);
}

最终可以达到的效果如下:

 使用采样点为150,在edge中测得FPS=5.3(disaster!)

五、遗留问题

  1. 对于PCSS,FPS只有5.3,如何能做到实时(至少30FPS)?,或者在实现上面还有可以优化的方法?
  2. 即便是5.3FPS的PCSS其实也只是做了近似,如w_{\text {Light}}、d_{\text {Blocker}},如果要更加精确地进行模拟,是否要涉及到射线和物体求交?性能开销难以想象
GAMES202homework:主要是学习闫老师的GAMES202高质量实时渲染完成的大作业 GAMES202homework 主要是学习闫老师的GAMES202高质量实时渲染完成的大作业 RenderDoc调试webgl: 用到了webgl所以需要webgl抓帧调试,但是当前shader的调试暂时不支持~ homework0 作业0主要是实现的Blinn-Phong 着色模型 ,最主要是的环境搭建测试熟悉202的框架等等; homework1 作业1主要是在Blinn-Phong 着色模型基础上实现阴影,通过shadow map实现硬阴影、Soft 阴影PCF算法过滤、软硬阴影PCSS; 立即下载

相关推荐

games202:games202作业

游戏202 games202作业

GAMES202 作业1实时阴影

正确的阴影对于渲染质量的提升起着举足轻重的作用。本文作为GAMES202作业1的笔记,记录学习内容和作业完成过程。

Carrot_Swordsman的博客 3073

GAMES202-HW-vulkano

GAMES202-HW-vulkano 该项目的目标是将的作业到Windows上的Rust和Vulkan平台。 IMGUI用于绘制UI控件。 跑步 遵循Windows。 该项目使用vulkano与Vulkan合作。 cargo run 。

[GAMES202]作业1

本文系列为介绍games202中作业1的任务介绍,部分框架代码介绍以及示例答案代码。硬阴影基于PCF的软阴影,和基于PCSS的软阴影实现。本文主要介绍其中硬阴影的作业部分。

刘好念的博客 1323

GEO数据挖掘实战:用R语言从GSE42872数据集中找到关键差异基因(附完整代码)

本文详细介绍了使用R语言从GSE42872数据集中挖掘关键差异基因的完整流程,包括数据下载、探针ID转换、差异表达分析和功能富集分析。通过实战代码演示,帮助生信研究者掌握GEO数据挖掘的核心技术,快速识别疾病相关基因并理解其生物学功能。

weixin_42625070的博客 365

从外包到转正:一个华为OD亲历者的真实工作体验与避坑指南

本文分享了一位华为OD员工从外包到转正的真实经历与实用建议。通过详细解析华为OD的日常工作、转正核心指标、机试备考技巧及职场突围策略,为读者提供了一份全面的避坑指南。特别针对机试E卷的高频考点和刷题方法,给出了具体的数据分析和实战经验。

weixin_33686714的博客 699

GAMES202 作业1

我们首先需要知道一件事情,就是在UV坐标中,坐标的范围为0到1,所以我们设定一个滤波框的大小,并且想象我们将shadow map除以其本身的大小,shadow map就变成了1的单位大小。那么我们的处理方式就是设定采样窗口和采样密度,然后再窗口内均匀的采样,再shadow map上如果采样点的深度小于当前的深度,那么认为是遮挡物,记录其平均的深度信息。其中参数c是我们可以调节的一个最终系数,而参数filterRadiusUV是当使用PCF时,自适应还得考虑PCF的采样范围,但我们实现目前暂时用不到。

m0_57958061的博客 1038

yandex 不定长旋转验证码PPOCR识别案例

可以看到,此种验证码非常变态,旋转角度不固定,干扰背景不固定,字符长度不固定,弯曲形变都是不固定的,在人眼都很难分辨验证码的情况下,其实都是对于模型来讲,也是比较困难的哈哈,传统验证码可能小几百张就能达到很好的效果了,是因为,验证码难度比较简单,通常都是4,6位定长,但是对于模型而言,无非就是增加数据量。由于该验证码的复杂度,自己写一个rcnn或者其他模型,来优化改数据集进行训练变的尤为困难复杂,所以这里强烈推荐百度飞桨的开源模型。在得到较好的loss后,导出模型。如有侵犯,请联系作者下架。

码王吴彦祖的博客 723

GAMES202——作业1 实时阴影(ShadowMap,PCF,PCSS)

获取对周围的随机采样方向,乘以采样半径大小再除以贴图大小即可求得采样点在贴图中的位置。方法,第一次先以光源为视点位置,将光源能看见的所有物体进行光栅化,并将以深度值进行绘制的结果保存到帧缓冲区,帧缓冲区的内容写入纹理。第二次绘制,根据纹理和一个将世界坐标下的点转化到以光源为视点的坐标系的矩阵,判断是否被遮挡,从而实现硬阴影。因为是计算遮挡物的平均深度,所以没有遮挡物的话,返回-1处理,有遮挡物返回遮挡物的平均深度,而不是返回整个采样区域的平均深度。如果查到的深度比现在的点的深度小,说明现在的点被遮挡。

weixin_50780569的博客 1749

项目报告:基于Sentaurus TCAD的多沟道长度NMOS器件仿真、SPICE模型提取与环形振荡器电路验证

本项目基于Sentaurus TCAD工具,完成了从器件仿真到电路验证的全流程研究。首先构建了45nm、65nm和90nm三种沟道长度的NMOS器件,通过TCAD仿真分析了其电学特性,观察到明显的短沟道效应。随后提取了BSIM4 SPICE模型参数,并生成Hspice可用的模型文件。最后利用该模型设计了11级环形振荡器进行电路验证,证明了模型的实用性。研究结果表明,随着沟道长度缩短,器件性能虽有所提升,但短沟道效应导致关态电流显著增加,影响整体能效。本项目为纳米尺度MOSFET的建模与电路设计提供了完整的技

huanghm88的专栏 284

万字长文带你由浅入深夯实ARM汇编基础——汇编指令及寻址方式最全梳理(附示例)!

见到汇编就害怕?读完这篇,扫除汇编拦路虎!!(示例丰富,可做手册,随手查阅!)

车载系统攻城狮 1万+

games202 作业1原理实现与效果展示

games202作业1原理及代码实现

m0_47492055的博客 788

C#上位机之海康相机开发(SDK)实战教程

SDK的接口设计遵循“句柄-操作-释放”模式,掌握→→→的流程,就能完成基础开发;工业场景需重点关注稳定性:断连重连、内存管理、抗干扰(网口相机的IP设置、触发信号的可靠性);进阶功能(如图像处理、多相机同步)需结合视觉库(EmguCV)和硬件特性(GPIO触发),但前提是保证基础采集的稳定。建议新手先跑通SDK自带的C#示例,理解每个函数的作用,再逐步替换为自己的代码。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 2193

GAMES 202 - 作业 1: 实时阴影

GAMES 202 - 学习笔记(03) 作业 1: 实时阴影GAMES 202 - 学习笔记(03)作业 1: 实时阴影总览Shadow MapPCFPCSS结果 原课程视频链接以及官网 b站视频链接: https://www.bilibili.com/video/av887241709/. 课程官网链接: https://sites.cs.ucsb.edu/~lingqi/teaching/games202.html. 作业 1: 实时阴影 总览 针对阴影的渲染方程定义如下,右边第一个乘积项为阴影部分

qq_36242312的博客 4534

YOLOv11改进策略【损失函数篇】| arXiv2025 TeLU双曲正切指数线性单元 强梯度防消失 + 解析光滑稳训练,极速提升收敛与泛化

本文记录的是。TeLU(Hyperbolic Tangent Exponential Linear Unit)通过。本文利用TeLU激活函数,以x⋅tanhex统一实现正区间强梯度加速收敛与负区间持续梯度避免死神经元,全程保持解析光滑与极低计算开销,,显著提升深度网络在分类、检测、分割、时序任务中的收敛速度、精度与泛化鲁棒性。

Limiiiing的博客 224

第二天 原来Labview语言结构也挺自然(2.1 顺序结构)

新建项目文件StructureTest.lvproj,新建四个子VI(FlatSequence.vi、CaseStructure.vi、LoopStructure.vi、EventStructure.vi),具体新建流程可以参考1.2节。

weixin_38637405的博客 1004

GAMES202 作业1解答

作业要求: hw1.pdf 作业整体概述: 本次作业主要分为三个部分:SM,PCF,PCSS。 我们可以使用这个浏览器插件来监看实时帧率。(本人使用的是火狐浏览器) 同时我们在实现阴影的过程中可以Ctrl+shift+ESC呼出任务管理器,实时监看CPU的负载(对于我这个电脑小白来说蛮有意思的)。 作业框架整体解读: 框架解读我参考的是这个老哥的,写的非常清楚了,非常感谢!!。 第三方库 gl-matrix-min.js 矩阵及向量库 项目主页: https://glmatrix.net/ 文

qq_58622402的博客 4076

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

GAMES202作业1

GAMES202作业1笔记

runmasterdd的博客 603

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Games202作业1 Unity(更新完毕)

介绍 作业1在Unity上的复现, 实现了基于的硬阴影, PCF, PCSS 代码下载: https://github.com/Eagle104fred/Games202_Homework1 注意事项 阴影的shader必须写在阴影的接受物体上例如墙壁等地方, 和模型本身的shader没啥关系 注意深度像机的参数设置 实现过程 1.构建shadowMap以及硬阴影 新建unity场景后建立一个Camera, 也可以在下面创建一个光源给模型打光不过不影响阴影效果。 新建一个产生阴影的Object和一个

Eagle的博客 5383

Games101-202作业1

Games101作业1

qq_24917263的博客 697

GAMES202作业1-实现过程详细步骤

记录了games202作业1的shadowmap、PCF和PCSS的实现过程。

qq_41835314的博客 4847

games202作业1

【代码】games202作业1。

weixin_45415576的博客 363

GAMES202作业1-万字分析代码框架&帮助更好理解框架内容

还算比较全面的介绍了GAMES202作业1作业框架的内容,详细的注释了自己对于每一部分的理解。

qq_41835314的博客 4146
上一篇: Games202作业0及优化部分
下一篇: 重新思考C++函数重载
要什么单车
博客等级 码龄8年 3粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值