實現(xiàn)用戶裁切平面(three.js webgl_clipping例子)

three.js中的webgl_clipping例子實現(xiàn)了針對指定用戶裁切平面進行裁切的功能肠鲫。在現(xiàn)代圖形管線中干毅,通過API指定用戶裁切平面的功能已經(jīng)廢棄。但是這個功能很容易在shader中實現(xiàn)院溺。

實現(xiàn)過程

webgl_clpping例子首先實現(xiàn)了環(huán)結幾何體(torusknot geometry)數(shù)據(jù)的生成功能眉菱,接著使用phong shading對環(huán)結幾何體進行渲染。隨后指定用戶裁切平面拐格,在眼睛坐標空間對幾何體進行裁切率拒。(three.js在此處借助自己的框架實現(xiàn)優(yōu)勢,實現(xiàn)了更加復雜的場景禁荒。在例子中不僅輕松添加了陰影效果猬膨,并同時實現(xiàn)了對用戶裁切平面功能在所有shader上的動態(tài)支持)

模仿webgl_clipping例子,只從實現(xiàn)用戶裁切平面的角度呛伴,使用C++和OpenGL ES 3.0獲得了如下的渲染效果勃痴,iOS版本實現(xiàn)源碼可以從github上獲取。

userclipping_effect_20200403.jpg

Torusknot(環(huán)結)幾何體生成和渲染

webgl_clipping使用程序生成了torus knot幾何體數(shù)據(jù)热康,并對其使用phong shading方式進行渲染沛申。

1. Torus knot幾何體數(shù)據(jù)的生成

環(huán)結幾何體使用三角形幾何公式依據(jù)不同的旋轉弧度計算生成。代碼如下:

/**
* class TorusKnotGeometry constructor
* @param radius 整個torus knot環(huán)形結幾何體的半徑
* @param tube   環(huán)形結輪廓管道半徑
* @param tubularSegments 管道的數(shù)據(jù)段數(shù)
* @param radialSegments  環(huán)形幾何體橫截面的數(shù)據(jù)段數(shù)
* @param p 環(huán)形幾何體圍繞中心軸扭轉的圈數(shù)
* @param q 環(huán)形幾何體內部圓環(huán)的數(shù)量
*
*/
TorusKnotGeometry(float radius=1.0,float tube=0.4,int tubularSegments=64,int radialSegments=8,int p=2,int q=3){
    
    ...
    
    // helper variables
    Cvec3 vertex;
    Cvec3 normal;
    
    Cvec3 P1,P2;
    
    Cvec3 B,T,N;
    
    // 生成頂點姐军,法線和紋理坐標
    // tubularSegments為環(huán)結幾何體圍繞圍繞中心軸旋轉p圈的角度上所分的段數(shù)
    for (int i = 0; i <= tubularSegments; ++ i) {
 
        // u為每個管道端所占據(jù)的弧度radian铁材,用以計算當前分段位置出環(huán)形曲面上的位置
        float u = (float)i / tubularSegments * p * M_PI * 2;
        
        //P1為當前曲面位置上的點,P2為稍微靠前一點弧度曲面上的點奕锌。
        //這兩個點用于生成一個特定的”坐標系“著觉,用于計算正確的頂點位置。
        Cvec3 P1 = calculatePositionOnCurve(u, p, q, radius);
        Cvec3 P2 = calculatePositionOnCurve(u + 0.01, p, q, radius);
        
        //計算出正交標準化的切面空間坐標系[T,B,N]
        //T為P1點上的切線矢量
        Cvec3 T = P2-P1;
        //將P1和P2作為從環(huán)結幾何體坐標系原點而來的矢量惊暴,計算出半路half-way矢量作為法線計算的輔助矢量
        Cvec3 N = P2+P1;
        //計算出半法線bi-normal矢量
        Cvec3 B = cross(T, N);
        //再計算出真正的法線normal矢量
        N = cross(B, T);
        
        //標準化 B, N, T饼丘。
        B.normalize();
        N.normalize();
        T.normalize();
        
        for (int j = 0; j <= radialSegments; ++ j ) {

            //注意此處我們在xy-平面塑造形狀,無需計算z-值辽话。
            //環(huán)結幾何體圍繞中心軸旋轉弧度分段中肄鸽,每一段的弧度radians
            float v = j / (float)radialSegments * M_PI * 2;
            float cx = -tube * cos(v);
            float cy = tube * sin(v);
            

            //計算添加圍繞環(huán)結中心軸(z軸)旋轉的頂點的最終值
            Cvec3 vertex;
            
            vertex[0] = P1[0] + (cx * N[0] + cy * B[0]);
            vertex[1] = P1[1] + (cx * N[1] + cy * B[1]);
            vertex[2] = P1[2] + (cx * N[2] + cy * B[2]);
            
            vertices.push_back(vertex);
            
            //P1總是位于相關被計算頂點的中心,據(jù)此計算法線
            Cvec3 normal=vertex - P1;
            normal.normalize();
            
            normals.push_back(normal);
            
            //紋理坐標的計算
            Cvec2 uv(i / (float)tubularSegments,j / (float)radialSegments);
            uvs.push_back(uv);
            
        }
        
    }
    
    //生成繪制頂點的索引集合
    for (int j = 1; j <= tubularSegments; j ++ ) {
        
        for (int i = 1; i <= radialSegments; i ++ ) {
            
            //索引值
            int a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
            int b = ( radialSegments + 1 ) * j + ( i - 1 );
            int c = ( radialSegments + 1 ) * j + i;
            int d = ( radialSegments + 1 ) * ( j - 1 ) + i;
            
            //三角形面
            //indices.push( a, b, d );
            indices.push_back(a);
            indices.push_back(b);
            indices.push_back(d);
            //indices.push( b, c, d );
            indices.push_back(b);
            indices.push_back(c);
            indices.push_back(d);
            
        }
        
    }

幾何數(shù)據(jù)生成后油啤,生成對應的vertex buffer object和index buffer object典徘,使用GL_TRIANGLES圖元(primitives)類型進行繪制,使用phong shading方式進行著色渲染益咬,效果如下:

torus_knot_geometry_20200403.jpg

用戶裁切平面的實現(xiàn)

在OpenGL ES 1.1和OpenGL版本中提供固定功能方式的API用于實現(xiàn)裁切平面逮诲。但是當使用現(xiàn)代渲染管線(shader方式)時,并不能使用這個API,不過這個功能在shader中很容易實現(xiàn)汛骂。實現(xiàn)原理使用平面的數(shù)學知識。一個平面可以使用公式Ax+By+Cz=d來指定评腺,公式中(A,B,C)為平面的法線帘瞭,隨后使用Dist = (A × P·x) + (B × P·y) + (C × P·z) + D來計算點P到這個平面的距離,如果Dist>=0蒿讥,則認為這個點位于這個平面所指定的半空間之內蝶念,不需要被裁切,如果Dist<0,則認為這個點位于指定的半空間之外芋绸,需要被裁切媒殉。

在shader中實現(xiàn)上面的思路時,通常在眼睛坐標系中計算裁切平面摔敛,這種裁切效果比較符合人的經(jīng)驗預期廷蓉。shader相關代碼如下:

//vertex shader
...
//uniform變量 -- 4X4模型視圖矩陣
uniform  mat4      uModelViewMatrix;
//眼睛坐標指定的用戶裁切平面數(shù)據(jù)
uniform  vec4       uUserClipPlane;

//頂點位置
layout(location = 0) in vec3 myVertex;

//頂點到平面的距離變量
out float vDistance;

void main(void){
    vec4 p = vec4(myVertex,1);
    //轉換頂點
    vec4 eyePos = uModelViewMatrix * p;
    vec3 ecPosition = eyePos.xyz;
    
    //計算到user clip plane的距離
    vDistance = dot(ecPosition,uUserClipPlane.xyz)+uUserClipPlane.w;
    ...
 }
//fragment shader
...
out vec4 fragColor;

void main(){
      //如果位于指定半空間之外,則廢棄這個像素
    if(vDistance<0.0)
        discard;
    ...
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末马昙,一起剝皮案震驚了整個濱河市桃犬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌行楞,老刑警劉巖攒暇,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異子房,居然都是意外死亡形用,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門证杭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扣孟,“玉大人,你說我怎么就攤上這事久锥〈淳螅” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵琢歇,是天一觀的道長兰怠。 經(jīng)常有香客問我,道長李茫,這世上最難降的妖魔是什么揭保? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮魄宏,結果婚禮上秸侣,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好味榛,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布椭坚。 她就那樣靜靜地躺著,像睡著了一般搏色。 火紅的嫁衣襯著肌膚如雪善茎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天频轿,我揣著相機與錄音垂涯,去河邊找鬼。 笑死航邢,一個胖子當著我的面吹牛耕赘,可吹牛的內容都是我干的。 我是一名探鬼主播膳殷,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼操骡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赚窃?” 一聲冷哼從身側響起当娱,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎考榨,沒想到半個月后跨细,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡河质,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年冀惭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掀鹅。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡散休,死狀恐怖,靈堂內的尸體忽然破棺而出乐尊,到底是詐尸還是另有隱情戚丸,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布扔嵌,位于F島的核電站限府,受9級特大地震影響,放射性物質發(fā)生泄漏痢缎。R本人自食惡果不足惜胁勺,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望独旷。 院中可真熱鬧署穗,春花似錦寥裂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至褐啡,卻和暖如春诺舔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背春贸。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工混萝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留遗遵,地道東北人萍恕。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像车要,于是被迫代替她去往敵國和親允粤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內容