GAMES202 作业1

news2024/11/13 15:10:23

参考:games202作业1

SM

首先是利用shadow map去生成尝试生成硬阴影。根据作业的要求

我们完成光源对物体的mvp矩阵

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,-150,150,-150,150,1e-2,400)
  mat4.multiply(lightMVP, projectionMatrix, viewMatrix);
  mat4.multiply(lightMVP, lightMVP, modelMatrix);

  return lightMVP;
}

利用shadow map判断阴影

根据作业需求

首先我们需要将坐标映射到NDC标准坐标系,物体的坐标经过MVP变换以后,其范围为[-1,1]所以我们需要对坐标进行处理。

void main(void) {

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

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

  vec3 phongColor = blinnPhong();

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

这里有一个很关键的函数,用来生成我们的比较深度,一开始我也以为直接利用z来进行深度的比较

  • pack() 函数是为了将一个 32 位浮点数精确地分解为 4 个 8 位的 RGBA 通道值,方便通过 GPU 纹理或颜色缓冲区进行存储和传输。所以shadow map中存储的是pack后的值。
  • unpack() 函数则是从存储的 RGBA 通道值中恢复出原来的浮点数值。所以我们通过uv坐标(ndc标准化以后)得到纹理值以后,在转为flaot的深度,与自身的深度比较,判断是否为阴影区域
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); //计算每个点的z值
  rgbaDepth -= rgbaDepth.gbaa * bitMask; // Cut off the value which do not fit in 8 bits
  return rgbaDepth;
}
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);
}
float useShadowMap(sampler2D shadowMap, vec4 shadowCoord){
  float mapDepth = unpack(texture2D(shadowMap, shadowCoord.xy));
  if(shadowCoord.z > mapDepth + EPS){
    return 0.0;
  }
  return 1.0;
}

结果

可以看到生成了一个比较硬的阴影

改进

可以看到产生了比较多的锯齿装摩尔纹。就是产生了自遮挡。也就是我们需要加上bias去让遮挡有一个缓冲范围。

参考: 自适应bias

参照此文章给出的公式

其中R是采样半径,这里再PCF的时候会用到。

由于自适应算法与视锥体大小和ShadowMap大小有关,我们直接用#define定义这两个数据。

#define SHADOW_MAP_SIZE 2048
#define FRUSTUM_SIZE 400

设计getShaderBias函数

float getShadowBias(float c, float filterRadiusUV){
  vec3 normal = normalize(vNormal);
  vec3 lightDir = normalize(uLightPos - vFragPos);
  float fragSize = (1.0 + ceil(filterRadiusUV)) * (FRUSTUM_SIZE / SHADOW_MAP_SIZE / 2);
  return max(fragSize, fragSize * (1.0 - dot(normal, lightDir))) * c;
}

其中参数c是我们可以调节的一个最终系数,而参数filterRadiusUV是当使用PCF时,自适应还得考虑PCF的采样范围,但我们实现目前暂时用不到。

float useShadowMap(sampler2D shadowMap, vec4 shadowCoord, float biasc, float filterRadiusUV){
  float mapDepth = unpack(texture2D(shadowMap, shadowCoord.xy));
  float cur_depth = shadowCoord.z;
  float bias = getShadowBias(biasc, filterRadiusUV);
  if(shadowCoord.z - bias > mapDepth + EPS){
    return 0.0;
  }
  return 1.0;
}
float bias = .4;
visibility = useShadowMap(uShadowMap, vec4(shadowCoord, 1.0), bias, 0.);

非常牛逼,建议还是好好看看原文

PCF

作业框架提供了uniformDiskSamples和poissonDiskSamples两个随机采样函数,我们调用即可,采样函数要求我们传入一个vec2变量作为随机种子,我们直接使用片元坐标即可,注意不要使用固定值,否则每次采样结果都是一样的。

也就是说,作业提供了框架可以返回单位圆内的随机值。那么我们可以设定采样率和采样的最大半径,实现在一个采样圆内采样。

我们首先需要知道一件事情,就是在UV坐标中,坐标的范围为0到1,所以我们设定一个滤波框的大小,并且想象我们将shadow map除以其本身的大小,shadow map就变成了1的单位大小。所以我们在uv上面进行采样的时候,实际上就是除以一个shaow map的大小,得到我们实际的采样的半径。

#define SHADOW_MAP_SIZE 2048.
#define FRUSTUM_SIZE 400.
float getShadowBias(float c, float filterRadiusUV);
//float useShadowMap(sampler2D shadowMap, vec4 shadowCoord, float biasc, float filterRadiusUV);

float PCF(sampler2D shadowMap, vec4 coords, float filterRadiusUV, float biasc) {
  poissonDiskSamples(coords.xy);
  float visibility = 0.0;
  float currentDepth = coords.z;
  float textureSize = 2048.0;
  float filterRadius = 5.;
  float bias = getShadowBias(biasc, filterRadiusUV);
  for(int i=0;i<NUM_SAMPLES;i++){
    vec2 offset = poissonDisk[i] * filterRadiusUV;
    float closeDepth = unpack(texture2D(shadowMap, offset + coords.xy));
    if(currentDepth - bias < closeDepth + EPS){
      visibility += 1.0;
    }
  }
  return visibility / float(NUM_SAMPLES);
}
void main(void) {

  float visibility;
  vec3 shadowCoord = vPositionFromLight.xyz / vPositionFromLight.w;
  shadowCoord.xyz = (shadowCoord.xyz + 1.0) / 2.0;
  float bias = .4;
  float filterRadius = 5.;
  float filterRadiusUV = filterRadius / float(SHADOW_MAP_SIZE);
  //visibility = useShadowMap(uShadowMap, vec4(shadowCoord, 1.0), bias, 0.);
  visibility = PCF(uShadowMap, vec4(shadowCoord, 1.0), filterRadiusUV, bias);
  //visibility = PCSS(uShadowMap, vec4(shadowCoord, 1.0));

  vec3 phongColor = blinnPhong();

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

结果

PCSS

我们知道PCF会得到一个纯软阴影,但是我们需要得到一个一定范围内为硬阴影,软阴影应该在没有阴影和硬阴影之间过渡。

计算平均遮挡物

首先我们需要计算平均遮挡物的深度。同时这里我们可以设定采样窗口的密度。那么我们的处理方式就是设定采样窗口和采样密度,然后再窗口内均匀的采样,再shadow map上如果采样点的深度小于当前的深度,那么认为是遮挡物,记录其平均的深度信息。

float findBlocker( sampler2D shadowMap,  vec2 uv, float zReceiver ) {
  poissonDiskSamples(uv);
  float count = 0.0;
  float depthAccu = 0.0;
  float filterRadius = 20.;
  float shadowSize = float(SHADOW_MAP_SIZE);
  float filterStride = 1.;//密度
  float filterRadiusUV = filterRadius / shadowSize * filterStride;
  for(int i = 0; i<BLOCKER_SEARCH_NUM_SAMPLES;i++){
    float closeDepth = unpack(texture2D(shadowMap, filterRadiusUV * poissonDisk[i] + uv));
    if(zReceiver > closeDepth + EPS){
      count+=1.0;
      depthAccu += closeDepth;
    }
  }
  if(count == 0.0){
    return -1.0;
  }
	return depthAccu / count;
}

这里返回-1,代表没有遮挡物,证明可见度为1.0.

接下来根据pcss的步骤进行

#define WLight 20.
float PCSS(sampler2D shadowMap, vec4 coords){

  // STEP 1: avgblocker depth
  float averageDepth = findBlocker(shadowMap, coords.xy, coords.z);
  if(averageDepth < 0.0){
    return 1.0;
  }
  // STEP 2: penumbra size
  float penumbra = (coords.z - averageDepth) * WLight / averageDepth;
  // STEP 3: filtering
  float filterRadiusUV = penumbra / float(SHADOW_MAP_SIZE);
  float biasc = .2;
  return PCF(shadowMap, coords, filterRadiusUV, biasc);

}

结果对比

只使用shadow map

使用pcf

使用pcss

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

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

相关文章

Bigemap GIS Office 2024注册机 全能版地图下载软件

对于需要利用GIS信息进行编辑、设计的用户来说&#xff0c;Bigemap GIS Office占有重要地位。用户可以使用Bigemap GIS Office作为工具进行设计、分析、共享、管理和发布地理信息。Bigemap GIS Office能实现多种数据流转、嵌入、融合以及更多地为用户提供数据的增强处理及多种分…

文心一言 VS 讯飞星火 VS chatgpt (351)-- 算法导论24.1 2题

二、证明推论24.3。推论 24.3 的内容是设 G ( V , E ) G(V,E) G(V,E)是一带权重的源结点为 s s s的有向图&#xff0c;其权重函数为 ω : E → R ω:\boldsymbol{E→R} ω:E→R。假定图 G G G不包含从源结点 s s s可以到达的权重为负值的环路&#xff0c;则对于所有结点 v ∈ …

完美转发、C++11中与线程相关的std::ref

目录 模板中的万能引用 std::forward实现完美转发 C11中与线程相关的std::ref 线程函数参数 用函数指针作为线程函数 用lambda表达式作为线程函数 模板中的万能引用 void Func(int& x) {cout << "左值引用" << endl; } void Func(int&&am…

spark之不同序列化对比

一&#xff0c;spark的rdd的序列话不同介绍 下面是使用不同序列化后的占用资源和数据大小 2&#xff0c;sparksql中序列化的区别 sparksql中使用序列化和不使用差别不大&#xff0c;英文sparksql中默认使用了encode自己实现的序列化方法&#xff0c;加上与不加序列化差别不大…

基于PHP+MySQL组合开发的在线客服源码系统 聊天记录实时保存 带完整的安装代码包以及搭建部署教程

系统概述 随着互联网技术的飞速发展&#xff0c;企业与客户之间的沟通方式日益多样化&#xff0c;在线客服系统作为连接企业与客户的桥梁&#xff0c;其重要性不言而喻。然而&#xff0c;市场上现有的在线客服系统往往存在成本高、定制性差、维护复杂等问题。针对这些痛点&…

Zabbix 6.4添加中文语言

/usr/share/zabbix/include/locales .inc .phplocale -agrep “zh_CN" yum install langpacks-zh_CN.noarch y y y

【千帆AppBuilder】零代码+组件+代码节点方式实现AI应用《法定退休年龄计算器》

欢迎来到《小5讲堂》 这是《千帆》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 背景创建应用基本信息角色指令引导信息 组件整体界面开始节点代码节…

大腾智能3D协同平台通过华为云云软件认证

在数字化浪潮的推动下&#xff0c;工业软件不仅是研发和生产的核心工具&#xff0c;更是创新突破的基础&#xff0c;正成为推动工业领域数字化转型的关键力量。 近日&#xff0c;深圳市大腾信息技术有限公司凭借在技术创新与产品优化方面的卓越表现&#xff0c;再次迎来里程碑…

望繁信科技受邀出席ACS2023,为汽车行业数智化护航添翼

2023年5月25-26日&#xff0c;ACS2023第七届中国汽车数字科技峰会在上海成功举行。此次峰会汇聚了众多汽车领域的顶级专家、产业链代表及企业高管&#xff0c;共同探讨当今汽车产业的转型与未来发展趋势。 作为唯一受邀的流程挖掘厂商代表&#xff0c;望繁信科技携最新行业优势…

对于C++继承中子类与父类对象同时定义其析构顺序的探究

思考这样一串代码的运行结果&#xff1a; #include <iostream> using namespace std; class Person { public:~Person() { cout << "~Person()" << endl; } }; class Student:public Person { public:~Student() { cout << "~Student(…

【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略

文章目录 C类与对象前言读者须知RVO 与 NRVO 的启用条件如何确认优化是否启用&#xff1f; 1. 按值传递与拷贝省略1.1 按值传递的概念1.2 示例代码1.3 按值传递的性能影响1.3.1 完全不优化 1.4 不同编译器下的优化表现1.4.1 Visual Studio 2019普通优化1.4.2 Visual Studio 202…

828华为云征文|基于华为云Flexus X实例快速搭建Halo博客平台

目录 前言 一、Flexus云服务器X介绍 1.1 Flexus云服务器X实例简介 1.2 Flexus云服务器X实例特点 1.3 Flexus云服务器X实例场景需求 二、Flexus云服务器X购买 2.1 Flexus X实例购买 2.2 购买MySQL加速镜像 2.3 重置密码 2.4 登录服务器 三、Flexus X实例安装Docker 3.1 系统版本…

小小扑克牌算法

1.定义一个扑克牌类Card&#xff1a; package democard; public class Card {public String suit;//表示花色public int rank;//表示牌点数Overridepublic String toString() {return "{"suit rank"}";}//实例方法&#xff0c;初始化牌的点数和花色public…

IBM 撤出背后:国内技术人如何突围?

近年来&#xff0c;随着全球科技行业的风起云涌&#xff0c;各大科技巨头不断调整业务布局&#xff0c;甚至撤出某些市场。近日&#xff0c;IBM宣布在中国市场进一步收缩&#xff0c;引起了业界的广泛关注。作为曾经的科技领军企业&#xff0c;IBM的撤退背后到底传递出什么样的…

Fipexide(FPX):植物组织培养中的新兴化学诱导剂AbMole

在植物科学领域&#xff0c;组织培养技术一直是探索植物发育机制和生物技术应用的强大工具。然而&#xff0c;尽管植物生长调节剂如生长素&#xff08;Auxin&#xff09;和细胞分裂素&#xff08;Cytokinin&#xff09;在促进植物愈伤组织形成和再分化中发挥了关键作用&#xf…

Vue 依赖注入组件通信:provide / inject 使用详解

引言 在 Vue.js 中&#xff0c;我们经常会遇到组件之间需要共享数据的情况。一种常见的解决方案是通过 props 和 $emit 事件来进行数据传递&#xff0c;但对于多层嵌套的组件结构或共享状态的场景&#xff0c;这种方式显得繁琐而不直观。 幸运的是&#xff0c;Vue.js 提供了一…

powerbi -L10-文件夹内的文件名

powerbi -L10-文件夹内的文件名 Folder.Contents letSource Folder.Contents("\\your_folder\ your_folder "),#"Removed Other Columns" Table.SelectColumns(Source,{"Name", "Date modified", "Folder Path"}), in#&q…

国庆头像制作教程,这几种方法轻松制作国庆头像

随着国庆佳节的临近&#xff0c;朋友圈里是不是已经开始弥漫着浓浓的节日气氛&#xff1f;想要让你的头像也加入这场盛宴&#xff0c;成为最吸睛的存在吗&#xff1f;别急&#xff0c;今天就为你揭秘4款超实用的头像制作神器&#xff0c;能够让你的头像显现出浓郁的国庆节气氛&…

Qt 模型视图(二):模型类QAbstractItemModel

文章目录 Qt 模型视图(二)&#xff1a;模型类QAbstractItemModel1.基本概念1.1.模型的基本结构1.2.模型索引1.3.行号和列号1.4.父项1.5.项的角色1.6.总结 Qt 模型视图(二)&#xff1a;模型类QAbstractItemModel ​ 模型/视图结构是一种将数据存储和界面展示分离的编程方法。模…

高性能分布式搜索引擎Elasticsearch详解

♥️作者&#xff1a;小宋1021 &#x1f935;‍♂️个人主页&#xff1a;小宋1021主页 ♥️坚持分析平时学习到的项目以及学习到的软件开发知识&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…