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

news2024/12/23 12:11:00

目录

任务

        ShadowMap

        PCF

        PCSS

实现 

       ShadowMap

        useShadowMap      

        PCF

        findBlocker

        PCSS

结果


任务

        ShadowMap
        1.在 ShadowMaterial.js 中需要向 Shader 传递正确的 uLightMVP 矩阵,该矩阵参与了第一步从光源处渲染场景从而构造ShadowMap 的过程。 你需要完成 DirectionalLight 中的CalcLightMVP(translate, scale) 函数,它会在 ShadowMaterial 中被调用,并将返回光源处的 MVP 矩阵绑定从而完成参数传递过程。
        2.需要完善 phongFragment.glsl 中的 useShadowMap(sampler2D shadowMap,vec4 shadowCoord) 函数。该函数负责查询当前着色点在 ShadowMap 上记录的深度值,并与转换到 light space 的深度值比较后返回 visibility 项(请注意,使用的查询坐标需要先转换到 NDC 标准空间 [0,1])。
        
        PCF
        1.需要完善 phongFragment.glsl 中的 PCF(sampler2D shadowMap, vec4 shadowCoord, float filterSize) 函数。使用作业框架提供的圆盘滤波核,框架中提供了泊松圆盘采样和均匀圆盘采样
两种采样函数。
        
        PCSS

        1.需要完善 phongFragment.glsl 中的 findBlocker(sampler2D shadowMap, vec2 uv, float zReceiver)。findBlocker 函数中需要完成对遮挡物平均深度的计算。

        2.完善PCSS(sampler2D shadowMap, vec4 shadowCoord) 函数。

        

实现 

       ShadowMap

        使用shadow map实现硬阴影,需要实现经典的 Two Pass Shadow Map 方法,第一次先以光源为视点位置,将光源能看见的所有物体进行光栅化,并将以深度值进行绘制的结果保存到帧缓冲区,帧缓冲区的内容写入纹理。第二次绘制,根据纹理和一个将世界坐标下的点转化到以光源为视点的坐标系的矩阵,判断是否被遮挡,从而实现硬阴影。

        

        第一个任务是求世界坐标转化为光源坐标的矩阵。

        在这里,采用glMatrix库的相关API。

        以光源为视点,计算方法就是刚开始学图形学的时候学的MVP矩阵。对于模型矩阵modelMatrix,该函数传入了两个向量,一个是位移translate一个是缩放scale,使用这两个向量,计算出模型矩阵。对于视图矩阵,直接用该类自带的光源属性的值传入API得到。对于投影矩阵,使用正交投影,因为能够方便判断深度值。zNear值最好不要设置为0,因为zNear为0在后面的PCSS中会带来不必要的麻烦。

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

        // Model transform
        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, -100, 100, -100, 100, 0.01, 500);

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

        return lightMVP;
    }
        useShadowMap      
         第二个任务是完善useShadowMap函数。
         在vertexShader中,已经预先帮我们计算好了每一个片元在以光源为视点的坐标的位置vPositionFromLight,因此在fragmentShader里便能专注实现功能。
        要使用阴影贴图,需在fragmentShader的main函数中,先将[-1,1]空间转化为[0,1]空间。
  vec3 shadowCoord = vPositionFromLight.xyz / vPositionFromLight.w;
  shadowCoord = (shadowCoord + 1.0) / 2.0;

        转换完后,[0,1]空间的xy坐标就能刚好对应贴图的uv坐标。在阴影贴图中使用uv坐标查询相应位置的深度。如果查到的深度比现在的点的深度小,说明现在的点被遮挡。

float useShadowMap(sampler2D shadowMap, vec4 shadowCoord){

  vec4 shadowColor = texture2D(shadowMap,shadowCoord.xy);
  float depth = unpack(shadowColor);
  float z = shadowCoord.z;
  if(z > depth + EPS ){
    return 0.0;
  }

  return 1.0;
}
        PCF

        第三个任务是实现PCF的功能。其实就是在原来ShadowMap的基础上,对周围进行采样,并将结果求平均。

        先预定义一些常量以方便下面的PCF和PCSS的运算

#define SHADOW_MAP_SIZE 2048    //阴影贴图大小
#define NEAR 0.01               //之前在计算正交投影矩阵的时候的zNear
#define LIGHT_SIZE 10.0         //光源在世界的大小
#define LIGHT_UV_SIZE 0.15      //光源在贴图上的大小

        开始先调用作业框架里自带的获取采样点的函数。获取对周围的随机采样方向,乘以采样半径大小再除以贴图大小即可求得采样点在贴图中的位置。在这里定义一个阻挡值来记录积累阻挡的数量。作业原代码,参数是没有sampleRadious的,这里是另外加上去的,方便控制采样区域大小。

float PCF(sampler2D shadowMap, vec4 coords ,float sampleRadious ) {

  poissonDiskSamples(coords.xy);

  float block = 0.0;
  for(int i =0;i<NUM_SAMPLES;i++){
    vec4 shadowColor = texture2D(shadowMap,coords.xy + poissonDisk[i] * sampleRadious/ float(SHADOW_MAP_SIZE) );
    float depth = unpack(shadowColor);
    float z = coords.z;
    if(z > depth + EPS ){
      block = block + 1.0;
     }
  }
  return 1.0 - block / float(NUM_SAMPLES);

}
        findBlocker

        该函数实现的功能是计算遮挡物的平均深度。

        要根据点到光源的距离来决定采样区域的大小,也就是W_{Penumbra},采用相似三角形来计算。得到采样区域的大小后,进行采样。因为是计算遮挡物的平均深度,所以没有遮挡物的话,返回-1处理,有遮挡物返回遮挡物的平均深度,而不是返回整个采样区域的平均深度。

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

  poissonDiskSamples(uv);

  int blockCnt = 0;
  float blockDepth = 0.0;

  float sampleSize = LIGHT_UV_SIZE * (vPositionFromLight.z - NEAR ) / vPositionFromLight.z;

  for(int i =0;i<NUM_SAMPLES;i++){
    vec4 shadowColor = texture2D(shadowMap,uv + poissonDisk[i] * 10.0 / float(SHADOW_MAP_SIZE) );
    float depth = unpack(shadowColor);
    if(zReceiver > depth + EPS ){
      blockCnt++;
      blockDepth = blockDepth + depth;
     }
  }
  if(blockCnt == 0)return -1.0;
  return blockDepth /float(blockCnt);

}
        PCSS

        有了上面的findBlolcker后,实现起来就非常简单,使用上面findblocker的数据,调用PCF即可。注意当avgDepth为-1时,即无遮挡,为特殊情况,直接返回1.0。

float PCSS(sampler2D shadowMap, vec4 coords){

  vec2 uv = coords.xy;
  float zReceiver = coords.z;

  // STEP 1: avgblocker depth
  float avgDepth = findBlocker(shadowMap,uv,zReceiver);

  if(avgDepth < 0.0)return 1.0;

  // STEP 2: penumbra size
  float penumbra = (zReceiver - avgDepth) / avgDepth * LIGHT_SIZE;
  // STEP 3: filtering
  return PCF(shadowMap,coords,penumbra);
}
void main(void) {

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

  float visibility;
  //visibility = useShadowMap(uShadowMap, vec4(shadowCoord, 1.0));
  //visibility = PCF(uShadowMap, vec4(shadowCoord, 1.0) , 10.0);
  visibility = PCSS(uShadowMap, vec4(shadowCoord, 1.0));

  vec3 phongColor = blinnPhong();

  gl_FragColor = vec4(phongColor * visibility, 1.0);
  //gl_FragColor = vec4(phongColor, 1.0);
}

结果

ShadowMap硬阴影

PCF实现软阴影

PCSS实现软阴影

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

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

相关文章

4 - ZYNQ 信号、接口与引脚

文章目录 1 ZYNQ信号、接口与引脚1.1 电源引脚1.2 PS信号引脚1.3 PL信号引脚1.4 PS和PL交互接口 1 ZYNQ信号、接口与引脚 ZYNQ的信号、接口与引脚如下图所示&#xff0c;主要分为&#xff1a; PS部分PL部分PS和PL交互部分PS和PL共用部分&#xff08;如JTAG&#xff09; 1.…

Disassembly窗口信息解读

如上图程序所示&#xff0c;程序运行流程为&#xff1a; 1.跳转至main所在地址 2.加载全局变量counter地址??main_2到R0 3.将变量值存入R0 4.R0自增指令载入 5.加载counter地址到R1 6.通过R1的地址&#xff0c;R0取到counter的值 ??main_1最终结果R0存值&#xff0c…

常用工具网站之Emoji

https://www.emojiall.com/zh-hans/all-emojis 这个网站是一个收集和展示所有表情符号的网站。用户可以在这里找到各种不同类型的表情符号&#xff0c;并了解它们的含义和用法。 举例说明&#xff1a;在这个网站上&#xff0c;可以找到不同类型的奖牌表情符号&#xff0c;例如…

谈到这个痛点,写C的和不写C的码农都沉默了

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/2246.html 作为一名在计算机软件行业工作的C工程师&#xff0c;我深知在高要求的内存管理环境中工作有多么艰难。内存分配与优化、避免内存泄漏&#xff0c;都是日常挑战…

Scrapy入门学习

文章目录 Scrapy一. Scrapy简介二. Scrapy的安装1. 进入项目所在目录2. 安装软件包Scrapy3. 验证是否安装成功 三. Scrapy的基础使用1. 创建项目2. 在tutorial/spiders目录下创建保存爬虫代码的项目文件3.运行爬虫4.利用css选择器Scrapy Shell提取数据例如: Scrapy 一. Scrapy…

2024谷歌开发者大会分享

前言&#xff1a; google一般会在每年的5月份在美国举行开发者大会&#xff0c;然后8-9月份会在中国也举办一次开发者大会&#xff0c;内容主题纲要是一致的&#xff0c;但是由于中国剧本的时间稍微晚一些&#xff0c;所以其内容会更新一些。 每年的开发者大会&#xff0c;会隐…

跟着B站前端面试总结回顾前端基础知识(二)

深拷贝与浅拷贝 在JavaScript中&#xff0c;深拷贝是一个常见的需求&#xff0c;特别是在处理复杂数据结构&#xff08;如对象、数组等&#xff09;时&#xff0c;需要确保原始数据不被修改。下面通过表格形式列出几种常见的深拷贝方法&#xff0c;并简要说明其优缺点。 方法…

【AI模型:追求全能还是专精?】

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《热点时事》 期待您的关注 目录 引言 ​编辑 一&#xff1a;AI模型的全面评估和比较 二&#xff1a;AI模型的专精化和可扩展性…

宝塔安装yasd 远程调试 swoole

一、服务器安装yasd 1.安装yasd 按照官方文档&#xff0c;Yasd调试器官方文档 yum install boost boost-develgit clone https://github.com/swoole/yasd.gitcd yasdphpize --clean && phpize && ./configure --with-php-config/www/server/php/74/bin/php…

筛质数(线性筛法)

线性筛法&#xff1a; 假设有一个非质数 x&#xff0c;那么这个数可以被表示为一个最小质因数和一个因子相乘的形式 如 x 12 &#xff0c;那么 x 2*6 其中&#xff1a;2 就是 12 的最小质因数&#xff0c; 6 就是另一个因子 线性筛法就是利用每个数的最小质因数筛掉这个非…

做谷歌seo花钱多吗?

一分钱一分货&#xff0c;哪怕你是自己一个人做谷歌seo&#xff0c;服务器&#xff0c;域名的成本也是成本&#xff0c;当然&#xff0c;花费可能还少&#xff0c;但其中的时间精力&#xff0c;以及有没有效果&#xff0c;你大可自己掂量一下 你如果只是想随便做做&#xff0c;…

SSM框架之Mybatis框架

前言 什么是框架&#xff1f; 框架就是对技术的封装&#xff0c;将基础的技术进行封装&#xff0c;便于程序员使用&#xff0c;提高开发效率 ssm框架是什么&#xff1f; ssm包括spring、springMvc、Mybatis&#xff0c;是后端企业级开发时会使用到的框架组合&#xff0c;在…

python实战一:合并多个Excel中数据

看不懂可以跟着视频学一下&#xff0c;关于基础课程项目也可以私聊我&#xff01; 视频源码&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/2055653f735b 之前我们已经学习了如何读取和写入Execl数据&#xff0c;今天我们来用一个实例来进行表格的合并。如下是 2021年…

离散数学中的逻辑基础(1)

目录 引言 1. 命题及其逻辑运算 2. 逻辑等价与范式 3. 逻辑推理规则 4. 逻辑问题练习 5. 总结 引言 逻辑是离散数学的核心概念之一&#xff0c;它用于精确描述数学命题并分析其关系。逻辑不仅是数学证明的基础&#xff0c;也是计算机科学中算法设计和编程的基石。本篇文…

【STM32】STM介绍

一、嵌入式与STM32 1.嵌入式 除了桌面PC之外&#xff0c;所有的控制类设备&#xff0c;都称之嵌入式。 1 ARM概述 1.1 历史 1978年&#xff0c;物理学家赫尔曼豪泽&#xff08;Hermann Hauser&#xff09;和工程师Chris Curry&#xff0c;在英国剑桥创办了CPU公司&#xff…

识别不到开发板串口问题(故事版)

最近电脑重新刷机了&#xff0c;很多东西都没了&#xff0c;很伤心。但也是锻炼自己基本功的能力。 我在用vscode开发ESP32的时候&#xff0c;发现一直识别不到串口&#xff0c;很纳闷&#xff0c;以为笔记本端口坏了&#xff1f; 转念一想觉得是因为没有加驱动 当时下面有一…

大模型学习应用 3: AutoDL 平台 transformers 环境搭建及模型部署使用(持续更新中)

之前我们学习了在和鲸的预配置好的平台上进行学习&#xff0c;在工作中并不现实&#xff0c;本期我们的目标是将已有模型部署到云端进行运行 配置环境&#xff1a;RTX 4090D(24GB) python 3.12(ubuntu22.04) 参考文章:AutoDL平台transformers环境搭建 目录 注册及选择算力新建…

E6000物联网主机:打造智慧楼宇的未来

智慧楼宇&#xff0c;作为现代建筑领域的璀璨明星&#xff0c;正以其独特的魅力和强大的功能改变着我们的生活和工作方式。它并非简单的建筑与技术的叠加&#xff0c;而是通过先进的信息技术和智能化系统的深度融合&#xff0c;实现对建筑内各项设施和服务的高效管理与优化。 智…

Mac怎么安装谷歌浏览器

谷歌浏览器凭借其强大的功能&#xff0c;成为广大用户的首选浏览器。其中Mac用户在进行下载和安装时&#xff0c;可能会出现一些困难。为了帮助大家顺利的在Mac系统中成功安装&#xff0c;下面就给大家详细分享Mac安装谷歌浏览器指南&#xff0c;希望对你有所帮助。 Mac安装谷歌…

xss-labs通关攻略 16-20关

第16关 ?keyword<img%0asrc"1.jpg"%0aοnerrοralert(1)> 第17关 查看源代码 输入 ?arg01a&arg02 οnmοuseοveralert("aini") 第18关 直接输入 ?arg01a&arg02 οnmοuseοveralert("aini") 第19关 需要下载一个flash 输…