首頁 > 軟體

threejs中使用drawbufferss範例詳解

2022-09-06 18:06:42

原因

深度剝離實現之後,似乎會使得走樣嚴重起來。 我意識到,這是因為 剝離這個過程,並沒有什麼講究,只要是深度小於等於就剔除了,這樣很可能就導致了,原本平滑的差值過渡出現了斷層,突變。 簡單的解決辦法就是增頂點數。

一番搜尋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的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com