WebGL之延迟着色
什么是延迟着色(Deferred Shading)?它是相对于正常使用的正向着色(Forward Shading)而言的,正向着色的工作模式:遍历光源,获取光照条件,接着遍历物体,获取物体的几何数据,最后根据光照和物体几何数据进行计算。
但是正向着色(Forward Shading)在光源非常多的情况下,对性能的消耗非常大。因为程序要对每一个光源,每一个需要渲染的物体,每一个需要渲染的片段进行迭代!还有片段着色器的输出会被之后的输出覆盖,正向渲染会在场景中因多个物体重合在一个像素上浪费大量的片段着色器运行时间。
延迟着色(Deferred Shading),就是为了解决上述问题而生,尤其是需要渲染几百上千个光源的场景。
本节实现的效果请看:延迟着色 deferred sharding

正向着色伪代码:
foreach light {
foreach visible mesh {
if (light volume intersects mesh) {
render using this material/light shader;
accumulate in framebuffer using additive blending;
}
}
}
延迟着色
延迟着色(Deferred Shading)工作模式就是将计算量大的渲染光照部分 延迟(Defer) 到后期进行处理,因此它包含两个处理阶段(Pass):
- 第一个 几何处理阶段(Geometry Pass) 是将对象的各种几何信息进行渲染并储存在叫做G缓冲(G-buffer)的纹理中。主要包括位置向量(Position Vector)、颜色向量(Color Vector)、法向量(Normal Vector)。这些储存在G缓冲中的几何信息将会用来做之后的光照计算。
- 第二个 光照处理阶段(Lighting Pass) 是对G缓冲中的几何数据的每一个片段进行光照计算。光照处理阶段不是直接将每个对象从顶点着色器带到片段着色器,而是对G缓冲中的每个像素进行迭代,从对应的G缓冲纹理获取输入变量。
延迟着色伪代码:
// g-buffer pass
foreach visible mesh {
write material properties to g-buffer;
}
// light accumulation pass
foreach light {
compute light by reading g-buffer;
accumulate in framebuffer;
}
帧缓冲
延迟着色(Deferred Shading) 的 G缓冲(G-buffer) 是基于 帧缓冲(frameBuffer) 实现的,涉及到高级应用,帧缓冲 真的是无处不在啊!该demo的几何处理阶段分别对位置(position),法向量(normal),颜色(color) 进行缓存,那么对应就要建立3个颜色附件,别忘了同时建立用于深度测试用的 深度缓冲(Z-Buffer)。
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
const fbo = {
framebuffer: fb,
textures: []
};
// 创建颜色纹理
for(let i = 0; i < 3; i++){
const tex = initTexture(gl, { informat: gl.RGBA16F, type: gl.FLOAT }, width, height);
framebufferInfo.textures.push(tex);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, tex, 0);
}
// 创建深度渲染缓冲区
const depthBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
多渲染目标 draw buffers
WebGL 实现多渲染目标需要打开 WEBGL_draw_buffers 这个扩展,但是 WebGL 2.0 直接就能使用的。我这里为了方便就基于 WebGL 2.0 来实现,多渲染目标调用方式如下:
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
着色器
因为延迟着色器分两个阶段,那么对应就需要两队着色器,首先来看几何处理阶段的着色器。
几何处理阶段 顶点着色器(vertex)
#version 300 es
in vec4 aPosition;
in vec4 aNormal;
uniform mat4 modelMatrix;
uniform mat4 vpMatrix;
out vec3 vPosition;
out vec3 vNormal;
void main() {
gl_Position = vpMatrix * modelMatrix * aPosition;
vNormal = vec3(transpose(inverse(modelMatrix)) * aNormal);
vPosition = vec3(modelMatrix * aPosition);
}
几何处理阶段 片段着色器(fragment),这里的三个输出变量对应就是帧缓冲中的三个颜色纹理。
#version 300 es
precision highp float;
layout (location = 0) out vec3 gPosition;// 位置
layout (location = 1) out vec3 gNormal; // 法向量
layout (location = 2) out vec4 gColor; // 颜色
uniform vec4 color;
in vec3 vPosition;
in vec3 vNormal;
void main() {
gPosition = vPosition;
gNormal = normalize(vNormal);
gColor = color;
}
接着就是光照处理阶段的着色器组了。
光照处理阶段 顶点着色器(vertex),这个非常简单,对应到帧缓冲,也就是个平面贴图而已。
#version 300 es
in vec3 aPosition;
in vec2 aTexcoord;
out vec2 texcoord;
void main() {
texcoord = aTexcoord;
gl_Position = vec4(aPosition, 1.0);
}
光照处理阶段 片段着色器(fragment),需要从对应的纹理贴图取出对应的几何数据。也就是使用 texture 函数结合贴图和 贴图坐标(texcoord) 就可以计算出对应的几何数据,接着就是根结合照进行渲染最终结果。
#version 300 es
precision highp float;
uniform vec3 viewPosition;
uniform vec3 lightDirection;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform float shininess;
// 各种自定义变量 ...
uniform sampler2D gPosition;// 位置
uniform sampler2D gNormal; // 法向量
uniform sampler2D gColor; // 颜色
in vec2 texcoord; // 坐标
out vec4 FragColor;
void main() {
vec3 fragPos = texture(gPosition, texcoord).rgb;
vec3 normal = texture(gNormal, texcoord).rgb;
vec3 color = texture(gColor, texcoord).rgb;
// todo: 各种计算过程...
// 环境光
vec3 ambient = ambientColor * color;
// 漫反射
// ...
vec3 diffuse = lightColor * color * cosTheta;
// 高光
// ...
vec3 specular = lightColor * specularIntensity;
FragColor = vec4(ambient + diffuse + specular, 1.0);
}
WebGL 流程
最后就是使用 JavaScript 将整个流程串起来,WebGL 的其他技术细节不再详细介绍了,具体可以看我之前的 WebGL 教程。这里介绍一下大体的流程:
- 几何处理阶段:绑定帧缓冲,切换到对应的program,设置各种变量,输出到帧缓冲;
- 光照处理阶段:切换回正常缓冲,切换到对应的program,设置各种变量,同时将前面几何处理阶段的得到纹理作为输入变量,输出渲染结果;
/**
* 场景绘制到帧缓冲区
*/
gl.bindFramebuffer(target, fbo.framebuffer); // 绑定帧缓冲
gl.viewport(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 清屏
gl.useProgram(program);
//采样到3个颜色附件(对应的几何纹理)
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
setUniforms(program, uniforms);// 设置uniform变量
setBuffersAndAttributes(gl, vao);// 设置缓存和attribute变量
drawBufferInfo(gl, vao); // 写入缓冲区
/**
* 从帧缓存渲染到正常缓冲区
*/
gl.bindFramebuffer(target, null); // 切换回正常缓冲
gl.viewport(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(fProgram);
const uniforms = {
// 其他变量 ...
gPosition: fbo.textures[0],// 位置纹理
gNormal: fbo.textures[1],// 法向量纹理
gColor: fbo.textures[2], // 颜色纹理
};
setUniforms(fProgram, uniforms);
setBuffersAndAttributes(gl, fVao);
drawBufferInfo(gl, fVao); // 输出画面
本节实现的效果请看:延迟着色 deferred sharding
demo 使用了1个平行光源,10个点光源,3个聚光灯实现了类似舞厅五彩斑斓的渲染效果。
最后
延迟着色(Deferred Shading) 在复杂光照条件下有着性能优势,但它也有缺点:大内存开销。还有在光源不是很多的场景中,延迟渲染并不一定会更快,甚至有时候由于开销过大还会变得更慢。当然在更复杂的场景中,延迟渲染会变成一个重要的性能优化手段。
WebGL之延迟着色的更多相关文章
- Deferred Shading 延迟着色(翻译)
原文地址:https://en.wikipedia.org/wiki/Deferred_shading 在3D计算机图形学领域,deferred shading 是一种屏幕空间着色技术.它被称为Def ...
- WebGL编程指南案例解析之多数据存储于一个缓冲区以及着色器通信
//顶点着色器往片元着色器传值 //多个参数值存于一个缓冲对象中 var vShader = ` attribute vec4 a_Position; attribute float a_PointS ...
- WebGL简易教程(二):向着色器传输数据
目录 1. 概述 2. 示例:绘制一个点(改进版) 1) attribute变量 2) uniform变量 3) varying变量 3. 结果 4. 参考 1. 概述 在上一篇教程<WebGL ...
- WebGL入门教程(五)-webgl纹理
前面文章: WebGL入门教程(一)-初识webgl WebGL入门教程(二)-webgl绘制三角形 WebGL入门教程(三)-webgl动画 WebGL入门教程(四)-webgl颜色 这里就需要用到 ...
- WebGL入门教程(四)-webgl颜色
前面文章: WebGL入门教程(一)-初识webgl WebGL入门教程(二)-webgl绘制三角形 WebGL入门教程(三)-webgl动画 颜色效果图: 操作步骤: 1.创建HTML5 canva ...
- WebGL入门教程(二)-webgl绘制三角形
前面已经介绍过了webgl,WebGL入门教程(一)-初识webgl(http://www.cnblogs.com/bsman/p/6128447.html),也知道了如何绘制一个点,接下来就用web ...
- Web3D编程入门总结——WebGL与Three.js基础介绍
/*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识 ...
- [js] webgl 初探 - 绘制三角形
摘要: 1. webgl 概念挺多的, 顶点着色器.片段着色器, 坐标 2. 绘制前期准备工作好多 目前看的比较好的教材: https://developer.mozilla.org/zh-CN/do ...
- Javascript高级编程学习笔记(96)—— WebGL(2) 类型化视图
类型化视图 类型化视图一般也被认为是一种类型化数组. 因为其元素必须是某种特定的数据类型,类型化视图都继承自 Dataview Int8Array: 表示8位二补整数(即二进制补数) Uint8Arr ...
随机推荐
- mysql表中已有数据,为表新增一个自增id。
第一步,在navicat中,例如表test新建查询,输入以下两行代码即可搞定. alter table test add id int; alter table `test` change id id ...
- python3 - 常用的操作数据库
# 获取手机号数据表的中的数据 sql2 = 'SELECT shoujihao FROM shoujihao' self.cursor.execute(sql2) sjh_dates = self. ...
- unity探索者之安卓微信登录,非第三方插件
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/7666348.html 之前写了两篇关于微信分享的博客,其实微信登录.分享.支付博主 ...
- 如何选择一台适合Java开发的电脑
前言 最近在群里有同学求推荐Java开发用的电脑,所以胖哥就出个简单的专题,用我贫瘠的电脑知识来帮助大家选择适合开发的电脑配置.因为家里的主机已经带不动两个 IDEA 了,更别提开个 Docker 啥 ...
- xfs文件系统修复方法
1. 前言首先尝试mount和umount文件系统,以便重放日志,修复文件系统,如果不行,再进行如下操作.fdisk -l 查看硬盘分区情况mount -l 查看文件系统挂载情况df -h 查看文件系 ...
- shell 三剑客之 grep
grep 的全称是 Globally search a Regular Expression and Print,是一种强大的文本搜索工具,它能使用特定模式匹配(包括正则表达式)搜索文本,并默认输出匹 ...
- Vue 通过调用百度API获取地理位置-经度纬度省份城市
一.首先在百度api注册获得ak密钥 二.新建js文件,我命名为loadBMap.js,里面创建script,代码如下: /** * 加载地图 * @param {Function} callback ...
- Java高级特性——反射机制(完结)——反射与注解
按照我们的学习进度,在前边我们讲过什么是注解以及注解如何定义,如果忘了,可以先回顾一下https://www.cnblogs.com/hgqin/p/13462051.html. 在学习反射和注解前, ...
- 焦大:SEO重思录(上)收录量和收录率的重新定位
http://www.wocaoseo.com/thread-198-1-1.html 前一段焦大在seo前线就看到有人问为何我收录量很大但是流量很低呢?有时候几百万的收录量但是流量却只有1000不到 ...
- e3mall商城的归纳总结6之redis
一.说在前面的话 前面几节我们主要对该项目的后端进行了增删改查,但是所有的数据都是存放在数据库中,这样的话数据库的压力显而易见是很大的,因此本节学习nosql的缓存,也就是redis的使用,在使用之前 ...