示例22:陰影

效果

image.png

代碼

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
    </head>

    <body>
        <canvas id="webgl" width="600" height="600">
            Please use a browser that supports "canvas"
        </canvas>

        <script src="./lib/webgl-utils.js"></script>
        <script src="./lib/webgl-debug.js"></script>
        <script src="./lib/cuon-utils.js"></script>
        <script src="./lib/cuon-matrix.js"></script>

        <script>
            let canvas = document.getElementById("webgl");
            let gl = getWebGLContext(canvas);

            // 陰影貼圖-頂點(diǎn)著色器
            let SHADOW_VSHADER_SOURCE =
                'attribute vec4 a_Position;\n' +
                'uniform mat4 u_MvpMatrix;\n' +
                'void main(){\n' +
                '   gl_Position = u_MvpMatrix * a_Position;\n' +
                '}\n';

            // 陰影貼圖-片元著色器
            let SHADOW_FSHADER_SOURCE =
                '#ifdef GL_ES\n' +
                'precision mediump float;\n' +
                '#endif\n' +
                'void main() {\n' +
                '  const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);\n' +
                '  const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);\n' +
                '  vec4 rgbaDepth = fract(gl_FragCoord.z * bitShift);\n' + // Calculate the value stored into each byte
                '  rgbaDepth -= rgbaDepth.gbaa * bitMask;\n' + // Cut off the value which do not fit in 8 bits
                '  gl_FragColor = rgbaDepth;\n' +
                '}\n';

            //頂點(diǎn)著色器
            let VSHADER_SOURCE =
                'attribute vec4 a_Position;\n' +
                'attribute vec4 a_Color;\n' +
                'uniform mat4 u_MvpMatrix;\n' +
                'uniform mat4 u_MvpMatrixFromLight;\n' +
                'varying vec4 v_PositionFromLight;\n' +
                'varying vec4 v_Color;\n' +
                'void main() {\n' +
                '  gl_Position = u_MvpMatrix * a_Position;\n' +
                '  v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n' +
                '  v_Color = a_Color;\n' +
                '}\n';

            //片元著色器
            let FSHADER_SOURCE =
                '#ifdef GL_ES\n' +
                'precision mediump float;\n' +
                '#endif\n' +
                'uniform sampler2D u_ShadowMap;\n' +
                'varying vec4 v_PositionFromLight;\n' +
                'varying vec4 v_Color;\n' +
                // Recalculate the z value from the rgba
                'float unpackDepth(const in vec4 rgbaDepth) {\n' +
                '  const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));\n' +
                '  float depth = dot(rgbaDepth, bitShift);\n' + // Use dot() since the calculations is same
                '  return depth;\n' +
                '}\n' +
                'void main() {\n' +
                '  vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' +
                '  vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n' +
                '  float depth = unpackDepth(rgbaDepth);\n' + // Recalculate the z value from the rgba
                '  float visibility = (shadowCoord.z > depth + 0.0015) ? 0.7 : 1.0;\n' +
                '  gl_FragColor = vec4(v_Color.rgb * visibility, v_Color.a);\n' +
                '}\n';

            let OFFSCREEN_WIDTH = 2048;
            let OFFSCREEN_HEIGHT = 2048;
            // 光源位置
            let LIGHT_X = 0;
            let LIGHT_Y = 40;
            let LIGHT_Z = 2;

            let ANGLE_STEP = 40; // The increments of rotation angle (degrees)
            let last = Date.now(); // Last time that this function was called

            let g_modelMatrix = new Matrix4();
            let g_mvpMatrix = new Matrix4();

            // 初始化陰影著色器
            let shadowProgram = createProgram(gl, SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE);
            shadowProgram.a_Position = gl.getAttribLocation(shadowProgram, 'a_Position');
            shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram, 'u_MvpMatrix');
            if (shadowProgram.a_Position < 0 || !shadowProgram.u_MvpMatrix) {
                console.log('Failed to get the storage location of attribute or uniform variable from shadowProgram');
                //return;
            }

            // 初始化常規(guī)繪畫著色器
            let normalProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE);
            normalProgram.a_Position = gl.getAttribLocation(normalProgram, 'a_Position');
            normalProgram.a_Color = gl.getAttribLocation(normalProgram, 'a_Color');
            normalProgram.u_MvpMatrix = gl.getUniformLocation(normalProgram, 'u_MvpMatrix');
            normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram, 'u_MvpMatrixFromLight');
            normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram, 'u_ShadowMap');
            if (normalProgram.a_Position < 0 || normalProgram.a_Color < 0 || !normalProgram.u_MvpMatrix ||
                !normalProgram.u_MvpMatrixFromLight || !normalProgram.u_ShadowMap) {
                console.log('Failed to get the storage location of attribute or uniform variable from normalProgram');
                // return;
            }

            let triangle = initVertexBuffersForTriangle(gl);
            let plane = initVertexBuffersForPlane(gl);

            let fbo = initFramebufferObject(gl);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, fbo.texture);

            gl.clearColor(0, 0, 0, 1);
            gl.enable(gl.DEPTH_TEST);

            let viewProjMatrixFromLight = new Matrix4();
            viewProjMatrixFromLight.setPerspective(70.0, OFFSCREEN_WIDTH / OFFSCREEN_HEIGHT, 1.0, 200.0);
            viewProjMatrixFromLight.lookAt(LIGHT_X, LIGHT_Y, LIGHT_Z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

            let viewProjMatrix = new Matrix4();
            viewProjMatrix.setPerspective(45, canvas.width / canvas.height, 1.0, 100.0);
            viewProjMatrix.lookAt(0.0, 7.0, 9.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

            let currentAngle = 0.0;
            let mvpMatrixFromLight_t = new Matrix4();
            let mvpMatrixFromLight_p = new Matrix4();
            let tick = function() {
                currentAngle = animate(currentAngle);

                gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); // Change the drawing destination to FBO
                gl.viewport(0, 0, OFFSCREEN_HEIGHT, OFFSCREEN_HEIGHT); // Set view port for FBO
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear FBO    

                gl.useProgram(shadowProgram); // Set shaders for generating a shadow map
                // Draw the triangle and the plane (for generating a shadow map)
                drawTriangle(gl, shadowProgram, triangle, currentAngle, viewProjMatrixFromLight);
                mvpMatrixFromLight_t.set(g_mvpMatrix); // Used later
                drawPlane(gl, shadowProgram, plane, viewProjMatrixFromLight);
                mvpMatrixFromLight_p.set(g_mvpMatrix); // Used later

                gl.bindFramebuffer(gl.FRAMEBUFFER, null); // Change the drawing destination to color buffer
                gl.viewport(0, 0, canvas.width, canvas.height);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear color and depth buffer

                gl.useProgram(normalProgram); // Set the shader for regular drawing
                gl.uniform1i(normalProgram.u_ShadowMap, 0); // Pass 0 because gl.TEXTURE0 is enabled銇欍倠
                // Draw the triangle and plane ( for regular drawing)
                gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_t.elements);
                drawTriangle(gl, normalProgram, triangle, currentAngle, viewProjMatrix);
                gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_p.elements);
                drawPlane(gl, normalProgram, plane, viewProjMatrix);

                window.requestAnimationFrame(tick, canvas);
            }
            tick();

            function drawTriangle(gl, program, triangle, angle, viewProjMatrix) {
                // Set rotate angle to model matrix and draw triangle
                g_modelMatrix.setRotate(angle, 0, 1, 0);
                draw(gl, program, triangle, viewProjMatrix);
            }

            function drawPlane(gl, program, plane, viewProjMatrix) {
                // Set rotate angle to model matrix and draw plane
                g_modelMatrix.setRotate(-45, 0, 1, 1);
                draw(gl, program, plane, viewProjMatrix);
            }

            function draw(gl, program, o, viewProjMatrix) {
                initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
                if (program.a_Color != undefined) // If a_Color is defined to attribute
                    initAttributeVariable(gl, program.a_Color, o.colorBuffer);

                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);

                // Calculate the model view project matrix and pass it to u_MvpMatrix
                g_mvpMatrix.set(viewProjMatrix);
                g_mvpMatrix.multiply(g_modelMatrix);
                gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

                gl.drawElements(gl.TRIANGLES, o.numIndices, gl.UNSIGNED_BYTE, 0);
            }

            // Assign the buffer objects and enable the assignment
            function initAttributeVariable(gl, a_attribute, buffer) {
                gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
                gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
                gl.enableVertexAttribArray(a_attribute);
            }

            function initVertexBuffersForPlane(gl) {
                // Create a plane
                //  v1------v0
                //  |        | 
                //  |        |
                //  |        |
                //  v2------v3

                // Vertex coordinates
                let vertices = new Float32Array([
                    3.0, -1.7, 2.5, -3.0, -1.7, 2.5, -3.0, -1.7, -2.5, 3.0, -1.7, -2.5 // v0-v1-v2-v3
                ]);

                // Colors
                let colors = new Float32Array([
                    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
                ]);

                // Indices of the vertices
                let indices = new Uint8Array([0, 1, 2, 0, 2, 3]);

                let o = new Object(); // Utilize Object object to return multiple buffer objects together

                // Write vertex information to buffer object
                o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
                o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
                o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
                if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null;

                o.numIndices = indices.length;

                // Unbind the buffer object
                gl.bindBuffer(gl.ARRAY_BUFFER, null);
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

                return o;
            }

            function initVertexBuffersForTriangle(gl) {
                // Create a triangle
                //       v2
                //      / | 
                //     /  |
                //    /   |
                //  v0----v1

                // Vertex coordinates
                let vertices = new Float32Array([-0.8, 3.5, 0.0, 0.8, 3.5, 0.0, 0.0, 3.5, 1.8]);
                // Colors
                let colors = new Float32Array([1.0, 0.5, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 0.0]);
                // Indices of the vertices
                let indices = new Uint8Array([0, 1, 2]);

                let o = new Object(); // Utilize Object object to return multiple buffer objects together

                // Write vertex information to buffer object
                o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
                o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
                o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
                if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null;

                o.numIndices = indices.length;

                // Unbind the buffer object
                gl.bindBuffer(gl.ARRAY_BUFFER, null);
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

                return o;
            }

            function initArrayBufferForLaterUse(gl, data, num, type) {
                // Create a buffer object
                let buffer = gl.createBuffer();
                if (!buffer) {
                    console.log('Failed to create the buffer object');
                    return null;
                }
                // Write date into the buffer object
                gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
                gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

                // Store the necessary information to assign the object to the attribute variable later
                buffer.num = num;
                buffer.type = type;

                return buffer;
            }

            function initElementArrayBufferForLaterUse(gl, data, type) {
                // Create a buffer object
                let buffer = gl.createBuffer();
                if (!buffer) {
                    console.log('Failed to create the buffer object');
                    return null;
                }
                // Write date into the buffer object
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);

                buffer.type = type;

                return buffer;
            }

            function initFramebufferObject(gl) {
                let framebuffer, texture, depthBuffer;

                // Define the error handling function
                let error = function() {
                    if (framebuffer) gl.deleteFramebuffer(framebuffer);
                    if (texture) gl.deleteTexture(texture);
                    if (depthBuffer) gl.deleteRenderbuffer(depthBuffer);
                    return null;
                }

                // Create a framebuffer object (FBO)
                framebuffer = gl.createFramebuffer();
                if (!framebuffer) {
                    console.log('Failed to create frame buffer object');
                    return error();
                }

                // Create a texture object and set its size and parameters
                texture = gl.createTexture(); // Create a texture object
                if (!texture) {
                    console.log('Failed to create texture object');
                    return error();
                }
                gl.bindTexture(gl.TEXTURE_2D, texture);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

                // Create a renderbuffer object and Set its size and parameters
                depthBuffer = gl.createRenderbuffer(); // Create a renderbuffer object
                if (!depthBuffer) {
                    console.log('Failed to create renderbuffer object');
                    return error();
                }
                gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);

                // Attach the texture and the renderbuffer object to the FBO
                gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);

                // Check if FBO is configured correctly
                var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
                if (gl.FRAMEBUFFER_COMPLETE !== e) {
                    console.log('Frame buffer object is incomplete: ' + e.toString());
                    return error();
                }

                framebuffer.texture = texture; // keep the required object

                // Unbind the buffer object
                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                gl.bindTexture(gl.TEXTURE_2D, null);
                gl.bindRenderbuffer(gl.RENDERBUFFER, null);

                return framebuffer;
            }

            function animate(angle) {
                let now = Date.now(); // Calculate the elapsed time
                let elapsed = now - last;
                last = now;
                // Update the current rotation angle (adjusted by the elapsed time)
                let newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
                return newAngle % 360;
            }
        </script>
    </body>
</html>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嵌莉,隨后出現(xiàn)的幾起案子郊愧,更是在濱河造成了極大的恐慌,老刑警劉巖厨疙,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凌摄,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狭归,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來文判,“玉大人过椎,你說我怎么就攤上這事∠凡郑” “怎么了疚宇?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵亡鼠,是天一觀的道長。 經(jīng)常有香客問我敷待,道長间涵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任榜揖,我火速辦了婚禮勾哩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘举哟。我一直安慰自己思劳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布炎滞。 她就那樣靜靜地躺著敢艰,像睡著了一般诬乞。 火紅的嫁衣襯著肌膚如雪册赛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天震嫉,我揣著相機(jī)與錄音森瘪,去河邊找鬼。 笑死票堵,一個(gè)胖子當(dāng)著我的面吹牛扼睬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悴势,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼窗宇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了特纤?” 一聲冷哼從身側(cè)響起军俊,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捧存,沒想到半個(gè)月后粪躬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昔穴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年镰官,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吗货。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泳唠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宙搬,到底是詐尸還是另有隱情笨腥,我是刑警寧澤孙援,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站扇雕,受9級特大地震影響拓售,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜镶奉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一础淤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哨苛,春花似錦鸽凶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亿蒸,卻和暖如春凑兰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背边锁。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工姑食, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茅坛。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓音半,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贡蓖。 傳聞我的和親對象是個(gè)殘疾皇子曹鸠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容