threejs中使用drawbufferss示例详解

原因

深度剥离实现之后,似乎会使得走样严重起来。 我意识到,这是因为 剥离这个过程,并没有什么讲究,只要是深度小于等于就剔除了,这样很可能就导致了,原本平滑的差值过渡出现了断层,突变。 简单的解决办法就是增顶点数。

一番搜寻weight oit算法的demo,但是只找到了用原生webgl写的,传送门。在费了几个日夜之后,终于看懂了,但是,还要把它用three实现。一般来说,没有使用编译的框架,应该可以直接使用原生,但是业务场景不允许。

我当然为此goole了一番,但是只找到一个教我如何在three里使用原生texture的方法, 算是解决了一个困境。

遇到的下一个困境就是,直接用原生的上下文进行一系列缓冲区和纹理的绑定操作是徒劳的, 因为three的renderer.render里面会有他的一套处理。 到这里我就意识到了,没法混用。

比如说,我要修改融合算法参数,使用gl.blendfunc ,但是只要我使用three的renderer.render ,它就会以材质上的相关属性重设blend,渲染目标也是如此。

至于为什么不能不用它的渲染器,直接使用gl.draw方法族,那当然是因为,拿不到全部的着色器数据。

所以,结论就是,必须完全使用threejs的方式实现。

历程

原生的使用

先来看原生的使用 , 用的是webgl2,glsl 3.0。 幸好, three用的也是,为此我还纳闷了一番。因为我发现three 仍然使用的是glsl 1.0的语法。

要知道,glsl 3.0 里面移除了 gl_fragcolor 这个内置输出变量,移除了 attribute varrying 关键字, 直接使用当然会报错。

因为,加权深度算法用的也是glsl3.0 ,我之前学的是1.0的语法和api,看的时候就有些云里雾里,后来发觉原来版本不对,立即去研究3.0的语法,然后很多问题就迎刃而解了。 可见,搞清楚自己用的api的版本的重要性

three的处理就是起别名, 对于fragment的输出,glsl 3.0,要求至少定义一个 out 修饰的 四维向量, 如果有多个,最好是用layout指定索引。 直接看代码,以片元着色器为例,顶点着色器不用输出颜色 。

#version 300 es
out highp vec4 ocolor; 
#define gl_fragcolor ocolor 
#define varying in

第一行指定版本

第二行定义输出变量

第三行定义ocolor的 别名为gl_fragcolor

第四行 定义修饰符 in的别名为 varying

知道了3.0的语法特点,再来看oit的代码。

基本流程

在初次绘制的着色器里 ,声明输出变量 颜色和透明度

 layout(location=0) out vec4 accumcolor;   
 layout(location=1) out float accumalpha;

声明两个纹理,并且和颜色缓冲区绑定,这样在绘制帧缓冲区时,会把颜色缓冲区的像素绘制到纹理。 color_attachmentn就是对应在片元着色器里声明的layout (loaction = n) out ve4

 accumbuffer = gl.createframebuffer();// 帧缓冲区唯一      
  gl.bindframebuffer(gl.framebuffer, accumbuffer);
 ......
 var accumtarget = gl.createtexture();
 ......
 gl.framebuffertexture2d(gl.framebuffer, gl.color_attachment0, gl.texture_2d, accumtarget, 0); 
var accumalphatarget = gl.createtexture();
......
gl.framebuffertexture2d(gl.framebuffer, gl.color_attachment1, gl.texture_2d, accumalphatarget, 0); 

在js里使用drawbuffer 将这两个输出变量和对应的缓冲区关联起来。没错这个方法只是关联而已,没有真的绘制。

gl.drawbuffers([ gl.color_attachment0, gl.color_attachment1 ]);
还原
gl.bindframebuffer(gl.framebuffer, null); 

原生就是这样实现,绘制之后颜色和透明度就绘制到对应的纹理了。 透明度是float 类型,可直接理解为一维向量。

然后再次绘制的时候就可以用这两个纹理的数据去修改alpha 使得最终的合成符合他那个公式,我也不理解这个公式,就这样吧。

灵光乍现

我理解了这个算法的一套流程,之后就发现文章开头的问题。我发现blend和 bindframebuffer的失灵之后,就去搜了一下对应的api ,然后断点看执行时机。

不知过了多久,也许一瞬,也许半天,我突然意识到,如果threejs实现了drawbuffers的封装,那么它必然使用了这个api,我直接搜 drawbuffers不就行了 。

于是我就发现了,rendertarget必须设置为 multiplerendertarget。 所以我转而搜这个,于是乎就发现了,有现成的example.

使用webglmultiplerendertargets

首先当然是实例化,需要传入纹理的尺寸和输出变量的数目。

rendertarget = new three.webglmultiplerendertargets(
		window.innerwidth * window.devicepixelratio,
		window.innerheight * window.devicepixelratio,
	2
	);

然后在绘制之前设置渲染目标。

	renderer.setrendertarget( rendertarget );

没了,就这么多。 至于纹理参数的设置,小细节不拘。

绘制后,就可以把 rendertarget.texture[ n ] 用three的方式传给着色器。 这里有一个小点要注意。

rendertarget.texture 没有复数。

以上就是threejs中使用drawbufferss示例详解的详细内容,更多关于threejs使用drawbufferss的资料请关注其它相关文章!

相关推荐:

Kivy 示例程序运行指南:正确安装与启动 kivy-examples

Kivy官方示例并非可直接导入的Python模块,而是独立的演示项目集合;安装后需手动进入对应目录,通过pythonmain.py等方式运行,而非尝试python-mkivy.examples。 kivy官方示例并非可直接导入的python模块,而是独立的演示项目集合;安装后需手动进入对应目录,通过...

如何正确运行 Kivy 官方示例程序

Kivy官方示例并非可直接导入的Python模块,而是独立的演示项目集合;安装后需手动进入对应目录,找到主程序(如main.py)并以普通脚本方式运行。 kivy官方示例并非可直接导入的python模块,而是独立的演示项目集合;安装后需手动进入对应目录,找到主程序(如`main.py`)并以普通脚本...

Python 实现限流算法示例

Python限流常用固定窗口、滑动窗口、令牌桶三种算法:固定窗口简单高效但有临界突发问题;滑动窗口精度高、内存开销略大;令牌桶支持突发流量,推荐生产使用,分布式场景建议Redis实现。 Python中实现限流(RateLimiting)常用算法有三种:固定窗口、滑动窗口、令牌桶。选择哪种取决于你对精...

Python递归遍历嵌套数据_递归实战示例

python中递归是处理嵌套数据结构最自然的方式,需明确终止条件、识别容器类型、避免无限递归和循环引用,并可通过深度限制与访问记录保障安全。 Python中递归是处理嵌套数据结构(如嵌套字典、列表、混合类型)最自然的方式之一。关键在于明确终止条件和递归调用的入口,避免无限递归或类型错误。 识别嵌套结...

Python写Excel文件_openpyxl实战示例

openpyxl核心是创建workbook、操作worksheet、写入值/公式、设置样式、保存文件:支持datetime自动识别、append高效批量写入、font/border等类设样式、可写公式及动态调列宽。 用openpyxl写excel文件,核心是创建工作簿、选中工作表、往单元格填值或插入...

Python多线程死锁示例_死锁产生条件解析

python多线程死锁源于多个线程循环等待对方持有的锁,核心是锁获取顺序不一致;需同时满足互斥、占有并等待、不可剥夺、循环等待四条件;避免方法包括统一加锁顺序和设置超时机制。 Python多线程死锁通常发生在多个线程互相等待对方持有的锁,且都不释放,导致所有相关线程永久阻塞。关键不在于“用了多线程”...