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

2024-08-28 22:12

本文主要是介绍GAMES202——作业1 实时阴影(ShadowMap,PCF,PCSS),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

任务

        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.jsCalcLightMVP(translate, scale) {let lightMVP = mat4.create();let modelMatrix = mat4.create();let viewMatrix = mat4.create();let projectionMatrix = mat4.create();// Model transformmat4.translate(modelMatrix, modelMatrix, translate);mat4.scale(modelMatrix, modelMatrix, scale);// View transformmat4.lookAt(viewMatrix, this.lightPos, this.focalPoint, this.lightUp);// Projection transformmat4.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 depthfloat avgDepth = findBlocker(shadowMap,uv,zReceiver);if(avgDepth < 0.0)return 1.0;// STEP 2: penumbra sizefloat penumbra = (zReceiver - avgDepth) / avgDepth * LIGHT_SIZE;// STEP 3: filteringreturn 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实现软阴影

这篇关于GAMES202——作业1 实时阴影(ShadowMap,PCF,PCSS)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1116031

相关文章

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob

Vuex Actions多参数传递的解决方案

《VuexActions多参数传递的解决方案》在Vuex中,actions的设计默认只支持单个参数传递,这有时会限制我们的使用场景,下面我将详细介绍几种处理多参数传递的解决方案,从基础到高级,... 目录一、对象封装法(推荐)二、参数解构法三、柯里化函数法四、Payload 工厂函数五、TypeScript

Vue3使用router,params传参为空问题

《Vue3使用router,params传参为空问题》:本文主要介绍Vue3使用router,params传参为空问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录vue3使用China编程router,params传参为空1.使用query方式传参2.使用 Histo

CSS Padding 和 Margin 区别全解析

《CSSPadding和Margin区别全解析》CSS中的padding和margin是两个非常基础且重要的属性,它们用于控制元素周围的空白区域,本文将详细介绍padding和... 目录css Padding 和 Margin 全解析1. Padding: 内边距2. Margin: 外边距3. Padd

CSS will-change 属性示例详解

《CSSwill-change属性示例详解》will-change是一个CSS属性,用于告诉浏览器某个元素在未来可能会发生哪些变化,本文给大家介绍CSSwill-change属性详解,感... will-change 是一个 css 属性,用于告诉浏览器某个元素在未来可能会发生哪些变化。这可以帮助浏览器优化

CSS去除a标签的下划线的几种方法

《CSS去除a标签的下划线的几种方法》本文给大家分享在CSS中,去除a标签(超链接)的下划线的几种方法,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧... 在 css 中,去除a标签(超链接)的下划线主要有以下几种方法:使用text-decoration属性通用选择器设置:使用a标签选择器,将tex

前端高级CSS用法示例详解

《前端高级CSS用法示例详解》在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交互和动态效果的关键技术之一,随着前端技术的不断发展,CSS的用法也日益丰富和高级,本文将深... 前端高级css用法在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交