diff --git a/Final_Project/final_project.css b/Final_Project/final_project.css index 0cf3d361fd844e388b5fb1a6c14829b0f9c59e04..e34e41ff6d1580503f929cb8aa34862bf42d1d52 100644 --- a/Final_Project/final_project.css +++ b/Final_Project/final_project.css @@ -6,7 +6,6 @@ body { background: #ebf9ff } - .layout-td { padding: 20px; /* background: pink; */ @@ -92,10 +91,19 @@ body { background-color: #9cd5fc; } -.code-box { - width: 300px; +#code-box { + visibility: hidden; + width: 450px; + height: 600px; background: #daf1ff; border: 1px solid #4f98c2; margin: 5px; padding: 10px; + overflow:scroll; +} + +#code-content { + font-size: 15px; + word-wrap: break-word; + white-space: pre-wrap; } \ No newline at end of file diff --git a/Final_Project/final_project.html b/Final_Project/final_project.html index 8904e3b868ffc1dc123434fe1141bb24af80a5b6..0c0a78865a6976fbb3ebeef603856154ae183fe0 100644 --- a/Final_Project/final_project.html +++ b/Final_Project/final_project.html @@ -13,18 +13,24 @@ </head> <body onload="main()"> - <H2> WebGL - After-images of rotating Cube </H2> - <p style="width:700px"> + <H1> WebGL - After-images of rotating Cube </H1> + <p style="width:800px"> This demo shows the after-images when the 3D cube rotates. Each after-image has a location coordinate where the cube was at previous frames. The older after-image, the fainter it is, and you can control the number of after-images. The after-image is saved every frame. If you change the frequency of after-images, you can set to display the after-image every n frames. </p> + <br><div> + <h2>About After-image</h2> + <p style="width:800px"> + In OpenGL, it 'draw' every frame. So in order to display the after-images, the location coordinate of the cube at that time should be stored every frame. In this demo, the set number of after-image(saved as the cube moves) is set number * frequency and the rest is deleted for saving memory. And after drawing the cube, it 'draw' after-image more and more transparent from the most recent saved. + </p> + </div> <table> <tr> <tr> <td class="layout-td"> - <canvas id="helloapicanvas" style="border: none;" width="500" height="500"></canvas><br><br> + <canvas id="helloapicanvas" style="border: none;margin-top: 50px" width="500" height="500"></canvas><br><br> <table border=2> <tr > <td id="matrix0"> <td id="matrix4"> <td id="matrix8"> <td id="matrix12"> @@ -38,14 +44,14 @@ </td> <td class="layout-td"> + <h3>Controls</h3> <div class="tab"> <button class="tablinks" id="clickedBtn" onclick="openControls(event, 'Translation')">Translation</button> <button class="tablinks" onclick="openControls(event, 'Rotation')">Rotation</button> <button class="tablinks" onclick="openControls(event, 'After-image')">After-image</button> </div> - <div id="Translation" class="tabcontent"> - <h3>Translation</h3> + <div id="Translation" class="tabcontent"><br> <button class="cont_btn" onclick="trXinc('+')">X + 0.1</button> <button class="cont_btn" onclick="trYinc('+')">Y + 0.1</button> <button class="cont_btn" onclick="trZinc('+')">Z + 0.1</button> @@ -59,8 +65,7 @@ <p id="webTrZ">Control Z of the Cube</p> </div> - <div id="Rotation" class="tabcontent"> - <h3>Rotation</h3> + <div id="Rotation" class="tabcontent"><br> <button class="cont_btn" onclick="animRotate('+')">Animation Rotate + 0.01</button><br> <button class="cont_btn" onclick="animRotate('-')">Animation Rotate - 0.01</button> <br> @@ -74,7 +79,6 @@ </div> <div id="After-image" class="tabcontent"> - <h3>After-image</h3> <p id="val_traceNum">The Number of After-image: 5</p> <div class="slidecontainer"> <input type="range" class="slider" min="1" max="10" value="5" class="slider" id="traceNum" onchange="changeTraceNum(this.value)"> @@ -93,26 +97,19 @@ </td> <td class="layout-td"> - <h3>major code</h3> - <button class="cont_btn2" onclick="changeTraceColor('color')">Original</button> - <button class="cont_btn2" onclick="changeTraceColor('white')">White</button> - <button class="cont_btn2" onclick="changeTraceColor('gray')">Gray</button> - <div class="code-box"> - <p id="code-content"> - This is space for major code. - </p> - </div> - <div class="code-box"> - <h4>About After-image</h4> - <p> - In OpenGL, it 'draw' every frame. So in order to display the after-images, the location coordinate of the cube at that time should be stored every frame. In this demo, the set number of after-image(saved as the cube moves) is set number * frequency and the rest is deleted for saving memory. And after drawing the cube, it 'draw' after-image more and more transparent from the most recent saved. - </p> + <h3>JavaScript code</h3> + <button class="cont_btn2" onclick="showCode('rendering')">Rendering</button> + <button class="cont_btn2" onclick="showCode('control')">Controls</button> + <button class="cont_btn2" onclick="showCode('shader')">Shader</button> + <div id="code-box"> + <pre id="code-content"> + </pre> </div> </td> </tr> </tr> </table> - + <p> (CC-NC-BY) 2019 JungYeun Won </p> </body> diff --git a/Final_Project/final_project.js b/Final_Project/final_project.js index 6a11f9fa33ec78182a605b363fb987fd75f739a5..bc7a00557a2072d7cb0b115ba2bd74065a3fa324 100644 --- a/Final_Project/final_project.js +++ b/Final_Project/final_project.js @@ -16,7 +16,6 @@ function testGLError(functionLastCalled) { } function initialiseGL(canvas) { - document.getElementById("clickedBtn").click(); try { // Try to grab the standard context. If it fails, fallback to experimental @@ -241,7 +240,7 @@ transX = 0.0; transY = 0.0; transZ = 0.0; frames = 1; var rotAxis = [1,0,1]; -function animRotate(sign) +function animRotate(sign) // increase or decrease rotation speed { if (sign=='+') animRotValue += 0.01; @@ -251,12 +250,12 @@ function animRotate(sign) document.getElementById("animRot").innerHTML = "Rotation speed: "+ animRotValue.toFixed(4); } -function animPause(){ +function animPause(){ // pause the rotating cube animRotValue = 0.0; document.getElementById("animRot").innerHTML = "Rotation speed: "+ animRotValue.toFixed(4); } -function changeRotAxis(axis){ +function changeRotAxis(axis){ // change rotating direction switch(axis){ case 'X': if (rotAxis[0]==1) @@ -328,13 +327,14 @@ function trZinc(sign) // translate Z increase or decrease document.getElementById("webTrZ").innerHTML = "Translation Z : " + transZ.toFixed(4); } +// About Trace(After-image) var trace = []; var traceNum = 5; var traceFreq = 5; var traceTotalNum = traceNum * traceFreq; var trace_alpha = 0.5/traceNum; -var slider_traceNum = document.getElementById("traceNum"); var traceColor = "color"; +var slider_traceNum = document.getElementById("traceNum"); function addTrace(mat){ if(trace.length>=traceTotalNum){ @@ -361,14 +361,12 @@ function changeTraceColor(type){ function renderScene() { - // console.log("Frame "+frames+"\n"); frames += 1 ; var locPmatrix = gl.getUniformLocation(gl.programObject, "Pmatrix"); var locVmatrix = gl.getUniformLocation(gl.programObject, "Vmatrix"); var locMmatrix = gl.getUniformLocation(gl.programObject, "Mmatrix"); - // uniform으로 GPU한테 던져줌 gl.uniformMatrix4fv(locPmatrix, false, proj_matrix); gl.uniformMatrix4fv(locVmatrix, false, view_matrix); // gl.uniformMatrix4fv(locMmatrix, false, mov_matrix); @@ -376,17 +374,18 @@ function renderScene() { if (!testGLError("gl.uniformMatrix4fv")) { return false; } + // gl.enable(gl.DEPTH_TEST); // gl.depthFunc(gl.LEQUAL); - gl.enable(gl.CULL_FACE); // 이거(Culling) 켜면 뒷면 안그림 + gl.enable(gl.CULL_FACE); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.blendEquation(gl.FUNC_ADD); - gl.clearColor(0.6, 0.8, 1.0, 1.0); // 배경색 설정 + gl.clearColor(0.6, 0.8, 1.0, 1.0); // set background color gl.clearDepth(1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.enableVertexAttribArray(0); // 좌표 + gl.enableVertexAttribArray(0); // coordinates gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 36, 0); gl.enableVertexAttribArray(1); // color gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 36, 12); @@ -397,7 +396,6 @@ function renderScene() { return false; } - // Earth glMatrix.mat4.identity(mov_matrix); glMatrix.mat4.rotate(mov_matrix,mov_matrix,rotValue, rotAxis); glMatrix.mat4.translate(mov_matrix, mov_matrix, [transX,transY,transZ]); @@ -407,7 +405,7 @@ function renderScene() { var mov_matrix_child = mov_matrix.slice(); addTrace(mov_matrix_child); - // original cube + // draw original cube gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData(1.0, "color")), gl.STATIC_DRAW); gl.uniformMatrix4fv(locMmatrix, false, trace[trace.length -1].slice()); @@ -424,7 +422,6 @@ function renderScene() { gl.drawArrays(gl.TRIANGLES, 0, 36); } - // 회전 수치 표 document.getElementById("matrix0").innerHTML = mov_matrix[0].toFixed(4); document.getElementById("matrix1").innerHTML = mov_matrix[1].toFixed(4); document.getElementById("matrix2").innerHTML = mov_matrix[2].toFixed(4); @@ -450,6 +447,7 @@ function renderScene() { function main() { var canvas = document.getElementById("helloapicanvas"); + document.getElementById("clickedBtn").click(); console.log("Start"); if (!initialiseGL(canvas)) { @@ -482,11 +480,199 @@ function main() { })(); } -function showCode(){ +// Show Javascript code on HTML +function showCode(type){ + document.getElementById("code-box").style.visibility = "visible"; var codeContent = document.getElementById("code-content"); + if(type=='rendering'){ + codeContent.innerHTML = +`// This is code of 'renderScene' function. +// This code run every frame and draw the original cube and after-image of it. + +// gl.enable(gl.DEPTH_TEST); +// gl.depthFunc(gl.LEQUAL); +gl.enable(gl.CULL_FACE); +gl.enable(gl.BLEND); +gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); +gl.blendEquation(gl.FUNC_ADD); +gl.clearColor(0.6, 0.8, 1.0, 1.0); // set background color +gl.clearDepth(1.0); +gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + +gl.enableVertexAttribArray(0); // coordinates +gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 36, 0); +gl.enableVertexAttribArray(1); // color +gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 36, 12); + +if (!testGLError("gl.vertexAttribPointer")) { + return false; +} + +glMatrix.mat4.identity(mov_matrix); +glMatrix.mat4.rotate(mov_matrix,mov_matrix,rotValue, rotAxis); +glMatrix.mat4.translate(mov_matrix, mov_matrix, [transX,transY,transZ]); +rotValue += animRotValue; +gl.uniformMatrix4fv(locMmatrix, false, mov_matrix); + +var mov_matrix_child = mov_matrix.slice(); +addTrace(mov_matrix_child); + +// draw original cube +gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer); +gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData(1.0, "color")), gl.STATIC_DRAW); +gl.uniformMatrix4fv(locMmatrix, false, trace[trace.length -1].slice()); +gl.drawArrays(gl.TRIANGLES, 0, 36); + +// draw trace of cube +color_a = 0.5; +for (var i = trace.length -1; i>=0;i-=traceFreq){ + color_a -= trace_alpha; + gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData(color_a, traceColor)), gl.STATIC_DRAW); + gl.uniformMatrix4fv(locMmatrix, false, trace[i].slice()); + if (color_a > 0.0) + gl.drawArrays(gl.TRIANGLES, 0, 36); +}`; + } + else if(type=="control"){ + codeContent.innerHTML = +`// This is code of functions that control basic parameters + +// translate X increase or decrease +function trXinc(sign) +{ + if(sign=='+') + transX += 0.01; + else + transX -= 0.01; +} + +// translate Y increase or decrease +function trYinc(sign) +{ + if(sign=='+') + transY += 0.01; + else + transY -= 0.01; +} + +// translate Z increase or decrease +function trZinc(sign) +{ + if(sign=='+') + transZ += 0.01; + else + transZ -= 0.01; +} + +// increase or decrease rotation speed +function animRotate(sign) +{ + if (sign=='+') + animRotValue += 0.01; + else + animRotValue -= 0.01; +} + +// pause the rotating cube +function animPause(){ + animRotValue = 0.0; +} + +// change rotating direction +// 'rotAxis' is an array that its length is 3. +// ex) rotAxis = [0,0,1] +function changeRotAxis(axis){ + switch(axis){ + case 'X': + if (rotAxis[0]==1) + rotAxis[0]=0; + else + rotAxis[0]=1; + break; + case 'Y': + if (rotAxis[1]==1) + rotAxis[1]=0; + else + rotAxis[1]=1; + break; + case 'Z': + if (rotAxis[2]==1) + rotAxis[2]=0; + else + rotAxis[2]=1; + break; + } + // clear trace array when change rotating direction + trace = []; +}`; + } + else { + codeContent.innerHTML = +`// This is code of 'initialiseShaders' fuction. + +var fragmentShaderSource = ' + varying mediump vec4 color; + void main(void) + { + gl_FragColor = 1.0 * color; + }'; + +gl.fragShader = gl.createShader(gl.FRAGMENT_SHADER); +gl.shaderSource(gl.fragShader, fragmentShaderSource); +gl.compileShader(gl.fragShader); +if (!gl.getShaderParameter(gl.fragShader, gl.COMPILE_STATUS)) { + alert("Failed to compile the fragment shader.\n" + gl.getShaderInfoLog(gl.fragShader)); + return false; +} + +var vertexShaderSource = ' + attribute highp vec3 myVertex; + attribute highp vec4 myColor; + uniform mediump mat4 Pmatrix; + uniform mediump mat4 Vmatrix; + uniform mediump mat4 Mmatrix; + varying mediump vec4 color; + void main(void) + { + gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(myVertex, 1.0); + color = myColor; + }'; + +gl.vertexShader = gl.createShader(gl.VERTEX_SHADER); +gl.shaderSource(gl.vertexShader, vertexShaderSource); +gl.compileShader(gl.vertexShader); +if (!gl.getShaderParameter(gl.vertexShader, gl.COMPILE_STATUS)) { + alert("Failed to compile the vertex shader.\n" + gl.getShaderInfoLog(gl.vertexShader)); + return false; +} + +gl.programObject = gl.createProgram(); + +// Attach the fragment and vertex shaders to it +gl.attachShader(gl.programObject, gl.fragShader); +gl.attachShader(gl.programObject, gl.vertexShader); + +// Bind the custom vertex attribute +gl.bindAttribLocation(gl.programObject, 0, "myVertex"); +gl.bindAttribLocation(gl.programObject, 1, "myColor"); + +// Link the program +gl.linkProgram(gl.programObject); + +if (!gl.getProgramParameter(gl.programObject, gl.LINK_STATUS)) { + alert("Failed to link the program.\n" + gl.getProgramInfoLog(gl.programObject)); + return false; +} + +gl.useProgram(gl.programObject);` + } + + } +// Show Control Tabs function openControls(evt, contName) { // Declare all variables var i, tabcontent, tablinks;