diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:17 -0500 |
commit | da78a9e329e272dedb2400b79a3bdeebff387d47 (patch) | |
tree | e6ef8aa7c43556f55ddfe031a01cf0a8fa271bfe /files/ko/web/api/webgl_api/tutorial | |
parent | 1109132f09d75da9a28b649c7677bb6ce07c40c0 (diff) | |
download | translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.gz translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.bz2 translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.zip |
initial commit
Diffstat (limited to 'files/ko/web/api/webgl_api/tutorial')
9 files changed, 1208 insertions, 0 deletions
diff --git a/files/ko/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html b/files/ko/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html new file mode 100644 index 0000000000..62ca452949 --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html @@ -0,0 +1,236 @@ +--- +title: WebGL 컨텍스트에 2D 컨텐츠 추가하기 +slug: Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL")}}</p> + +<p>일단 성공적으로 <a href="/en/WebGL/Getting_started_with_WebGL" style="font-size: 13.63636302948px; line-height: 19.0909080505371px;" title="en/WebGL/Getting started with WebGL">WebGL컨텍스트를 생성</a>하면 그 안에 렌더링을 시작할 수 있습니다. 가장 간단히 해볼 수 있는 것은 텍스쳐가 없는 2D 객체를 렌더링 하는 것입니다. 한번 사각형을 그리는 코드를 작성하는 것으로 시작해봅시다.</p> + +<h2 id="장면(scene)에_조명효과_추가하기">장면(scene)에 조명효과 추가하기</h2> + +<div> 시작 하기전에 반드시 이해해야할 것은 예제에서 오직 2차원 객체만 랜더링을 하더라도 3D 공간에서 그리고 있다는 것 입니다. 이처럼 여전히 객체가 그려질 간단한 장면에 조명 쉐이더를 설정하는것이 필요합니다. 여기서 어떻게 사각형에 조명효과를 줄 수 있는지 확인해보겠습니다.</div> + +<div> </div> + +<h3 id="쉐이더_초기화">쉐이더 초기화</h3> + +<p> <span style="font-size: 14px; line-height: 1.5;">쉐이더는</span><a class="external" href="http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf" style="font-size: 13.63636302948px; line-height: 19.0909080505371px;" title="http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf">OpenGL ES 쉐이딩 언어</a><span style="font-size: 14px; line-height: 1.5;">를 사용하여 지정됩니다. 컨텐츠의 유지보수와 업데이트를 쉽게하기 위해 쉐이더를 '불러오기위한' 코드를 직접 작성하겠습니다. 다시 말하자면 쉐이더를 직접 새로 만드는 것이 아니라 HTML문서에서 쉐이더를 '찾아오는' 자바스크립트 코드입니다. 이 작업을 처리하는 initShaders()함수의 일반적인 형태를 살펴보겠습니다.</span></p> + +<pre class="brush: js">function initShaders() { + var fragmentShader = getShader(gl, "shader-fs"); + var vertexShader = getShader(gl, "shader-vs"); + + // Create the shader program + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + // If creating the shader program failed, alert + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert("Unable to initialize the shader program."); + } + + gl.useProgram(shaderProgram); + + vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + gl.enableVertexAttribArray(vertexPositionAttribute); +} + +</pre> + +<p>이 방식을 통해 로딩되는 쉐이더는 두가지가 있습니다. 첫번째는 조각 쉐이더(fragment shader)로 "shader-fs"라는 ID를 가진 script 엘리먼트에서 불러옵니다. 두번째는 정점 쉐이더(vertex shader)로 "shader-vs"라는 ID를 가진 script엘리먼트에서 불러옵니다. getShader()함수는 다음 섹션에서 다룰 것입니다. 이 과정은 쉐이더 프로그램을 DOM에서 가저오는 것을 다룹니다. </p> + +<p>그 다음 우리는 WebGL 객체의 createProgram()함수를 호출하여 쉐이더 프로그램을 생성 할 것입니다. WebGL 객체에 두개의 쉐이더를 붙인다음 서로 연결할 것입니다. 그 다음 프로그램이 성공적으로 연결되었는지 확인하기 위해 gl 객체의 LINK_STATUS 매개변수를 체크합니다. 이것이 성공적이라면 새로운 쉐이더 프로그램을 활성화 합니다.</p> + +<h3 id="DOM에서_쉐이더_불러오기">DOM에서 쉐이더 불러오기</h3> + +<p>getShader()함수는 DOM에서 지정된 이름을 가진 쉐이더 프로그램을 가져와 컴파일된 쉐이더 프로그램을 호출자에 반환 합니다. 컴파일이나 불러올 수 없는 경우에는 null을 반환 합니다.</p> + +<pre class="brush: js">function getShader(gl, id) { + var shaderScript, theSource, currentChild, shader; + + shaderScript = document.getElementById(id); + + if (!shaderScript) { + return null; + } + + theSource = ""; + currentChild = shaderScript.firstChild; + + while(currentChild) { + if (currentChild.nodeType == currentChild.TEXT_NODE) { + theSource += currentChild.textContent; + } + + currentChild = currentChild.nextSibling; + } +</pre> + +<p>특정 ID를 가진 엘리먼트를 찾으면 텍스트 컨텐츠가 <code>theSource 변수에 저장됩니다.</code></p> + +<pre class="brush: js"> if (shaderScript.type == "x-shader/x-fragment") { + shader = gl.createShader(gl.FRAGMENT_SHADER); + } else if (shaderScript.type == "x-shader/x-vertex") { + shader = gl.createShader(gl.VERTEX_SHADER); + } else { + // Unknown shader type + return null; + }</pre> + +<p>쉐이더를 위한 코드가 읽혀지면 쉐이더가 정점 쉐이더(MIME type "x-shader/x-vertex")인지 조각 쉐이더(MIME type "x-shader/x-fragment")인지 결정하기 위해 쉐이더 객체의 MIME 형식을 살펴봅니다. 그 다음 소스 코드에서 얻어진 것을 가지고 적절한 타입의 쉐이더를 생성합니다.</p> + +<pre class="brush: js"> gl.shaderSource(shader, theSource); + + // Compile the shader program + gl.compileShader(shader); + + // See if it compiled successfully + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +} +</pre> + +<div><span style="font-size: 14px; line-height: 1.5;">마지막으로 소스는 쉐이더로 전달되고 컴파일됩니다. 만약 쉐이더가 컴파일하는 동안 에러가 발생하면 경고 메세지를 출력하고 null을 반환합니다. 그러지 않으면 새롭게 컴파일된 쉐이더가 호출자로 반환됩니다.</span></div> + +<div> </div> + +<h3 id="쉐이더">쉐이더</h3> + +<div>그 다음 쉐이더 프로그램을 HTML 표현에 추가해야 합니다. 쉐이더가 구체적으로 어떻게 작동하는지에 대한 내용은 이 문서에서 다루지 않습니다. 다음은 쉐이더 언어 문법입니다.</div> + +<div> </div> + +<h4 id="조각_쉐이더(Fragment_shader)">조각 쉐이더(Fragment shader)</h4> + +<p><span style="font-size: 14px; line-height: 1.5;">다각형 안에 있는 각각의 픽셀은 GL 전문용어로<strong> fragment</strong>이라고 부릅니다. fragment shader가 하는 일은 각 픽셀의 색상을 설정하는 것입니다. 우리는 간단하게 각 픽셀을 하얀색으로 지정하겠습니다.</span></p> + +<p>fragment의 색상에서 사용되는 gl_FragColor는 GL에서 만들어진 변수입니다. 아래와 같이 값을 설정하면 픽셀의 색상이 설정됩니다.</p> + +<pre class="brush: html"><script id="shader-fs" type="x-shader/x-fragment"> + + void main(void) { + + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + + } + +</script></pre> + +<div> </div> + +<h4 id="정점_쉐이더(Vertex_Shader)">정점 쉐이더(Vertex Shader)</h4> + +<p>정점 쉐이더는 각 정점의 위치과 모양을 정의합니다.</p> + +<pre class="brush: html language-html"><code class="language-html"><span class="script token"><span class="tag token"><span class="tag token"><span class="punctuation token"><</span>script</span> <span class="attr-name token">id</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>shader-vs<span class="punctuation token">"</span></span> <span class="attr-name token">type</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>x-shader/x-vertex<span class="punctuation token">"</span></span><span class="punctuation token">></span></span> + attribute vec3 aVertexPosition<span class="punctuation token">;</span> + + uniform mat4 uMVMatrix<span class="punctuation token">;</span> + uniform mat4 uPMatrix<span class="punctuation token">;</span> + + <span class="keyword token">void</span> <span class="function token">main<span class="punctuation token">(</span></span><span class="keyword token">void</span><span class="punctuation token">)</span> <span class="punctuation token">{</span> + gl_Position <span class="operator token">=</span> uPMatrix <span class="operator token">*</span> uMVMatrix <span class="operator token">*</span> <span class="function token">vec4<span class="punctuation token">(</span></span>aVertexPosition<span class="punctuation token">,</span> <span class="number token">1.0</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span> +<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>script</span><span class="punctuation token">></span></span></span></code></pre> + +<h2 id="sect1"> </h2> + +<h2 id="객체_생성">객체 생성</h2> + +<p>사각형 렌더링을 하기 전에 사각형의 각 정점들을 저장할 버퍼를 만들어야 합니다. 이를 <strong>initBuffers()</strong>라는 함수를 이용해 해보도록 하겠습니다. 앞으로 고급 WebGL 개념을 살펴보면서, 더욱 다양하고 복잡한 3D 오브젝트를 생성하고자 할 때 이 루틴을 많이 사용하게 될 것입니다.</p> + +<pre class="brush: js">var horizAspect = 480.0/640.0; + +function initBuffers() { + squareVerticesBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + + var vertices = [ + 1.0, 1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0 + ]; + + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); +} +</pre> + +<p><span style="font-size: 13.63636302948px; line-height: 19.0909080505371px;">이 예제에서는 장면(scene)의 기본적인 속성만을 보여주기 위해, 루틴이 다소 지나치게 단순화되어있습니다. 정점들을 저장할 버퍼를 얻기 위해 </span><code style="font-style: normal; font-size: 13.63636302948px; line-height: 19.0909080505371px;">gl</code><span style="font-size: 13.63636302948px; line-height: 19.0909080505371px;"> </span><span style="font-size: 13.63636302948px; line-height: 19.0909080505371px;">객체의 </span><span style="font-family: courier new,andale mono,monospace; font-size: 13.63636302948px; line-height: 19.0909080505371px;">createBuffer()</span><span style="font-size: 13.63636302948px; line-height: 19.0909080505371px;"> 메서드를 호출하는 것으로 시작합니다. 그 다음 </span><code style="font-style: normal; font-size: 13.63636302948px; line-height: 19.0909080505371px;">bindBuffer()</code><span style="font-size: 13.63636302948px; line-height: 19.0909080505371px;"> 메서드를 불러와 컨텍스트에 연결합니다.</span></p> + +<p>이 과정이 끝난 뒤 사각형의 각 정점 좌표를 담고있는 자바스크립트 배열을 생성합니다. 그런 다음 배열을 WebGL floats 배열로 변환한 뒤 gl객체의 bufferData() 메서드로 전달해 객체의 정점을 설정합니다.</p> + +<h2 id="장면(Scene)_그리기">장면(Scene) 그리기</h2> + +<p>쉐이더가 설정되고 객체가 생성되면 실제로 장면을 렌더링 할 수 있습니다. 이 예제에서 어떠한 애니메이팅도 안 할 것이기 떄문에 <span style="font-family: courier new,andale mono,monospace; font-size: 13.63636302948px; line-height: 19.0909080505371px;">drawScene()</span>함수는 매우 간단합니다. 이는 우리가 곧 다룰 몇 가지 유용한 루틴만 사용합니다?.</p> + +<pre class="brush: js">function drawScene() { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0); + + loadIdentity(); + mvTranslate([-0.0, 0.0, -6.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); + setMatrixUniforms(); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); +} +</pre> + +<p>첫번째 과정은 배경색에 컨텍스트를 clear하는 것입니다. 그 다음 카메라의 원근을 설정합니다. 시점을 45°로, 종횡비를 640/480(캔버스의 크기)로 설정합니다. 또한 카메라로부터 0.1에서 100단위 사이에 있는 오브젝트만 렌더링하도록 정합니다.</p> + +<p>그런 다음 identity(항등) position을 불러오고 카메라에서 6단위 만큼 translate(변환)하여 사각형의 위치를 정합니다 . 그 다음 사각형 정점 버퍼를 컨텍스트에 연결해 구성한 뒤 <span style="font-family: courier new,andale mono,monospace; font-size: 13.63636302948px; line-height: 19.0909080505371px;">drawArrays()</span> 메서드를 불러와 객체를 그립니다. </p> + +<p>브라우저가 웹GL을 지원한다면 다음 링크에서 확인 할 수 있습니다. <a href="/samples/webgl/sample2" title="https://developer.mozilla.org/samples/webgl/sample2">try out this demo by clicking here</a></p> + +<h2 id="행렬_유틸리티를_이용한_연산">행렬 유틸리티를 이용한 연산</h2> + +<p>행렬 연산은 꽤 복잡합니다. 행렬을 다루기 위한 코드를 직접 작성하고 싶은 사람은 아무도 없을 것입니다. 다행히도 벡터와 행렬 연산을 자바스크립트에서 다루는 데 아주 편리한 라이브러리인 <a class="external" href="http://sylvester.jcoglan.com/" style="font-size: 13.63636302948px; line-height: 19.0909080505371px;" title="http://sylvester.jcoglan.com/">Sylvester</a>가 있습니다.</p> + +<p>이 데모에서 사용한<span style="font-family: courier new,andale mono,monospace; font-size: 13.63636302948px; line-height: 19.0909080505371px;">glUtils.js</span> 파일은 웹에 떠돌아 다니는 많은 WebGL 데모에서 사용하고 있습니다. 이 파일이 어디서 나온 것인지 명확히 아는 사람은 아무도 없는것 같습니다만, HTML 로 출력하기 위한 메소드 뿐만 아니라 특별한 형태의 행렬을 만들기 위한 메소드를 추가되어 있어 Sylvester를 보다 정말 단순화 시켜놓았습니다.</p> + +<p>게다가 이 데모는 특정 작업을 위한 라이브러리를 이용할 때 도움될만한 몇가지 루틴을 정의하고 있습니다. 정확히 이것들이 어떤 일을 하는 것인지는 이 데모에서 다룰 범위는 아닙니다. 하지만 온라인에서 참고할만 한 좋은 레퍼런스가 많이 있습니다. 아래 {{ anch("See also") }} 섹션에 그 중 몇 개를 소개하고 있습니다.</p> + +<pre class="brush: js">function loadIdentity() { + mvMatrix = Matrix.I(4); +} + +function multMatrix(m) { + mvMatrix = mvMatrix.x(m); +} + +function mvTranslate(v) { + multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4()); +} + +function setMatrixUniforms() { + var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); + gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten())); + + var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); + gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten())); +} +</pre> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a class="external" href="http://mathworld.wolfram.com/Matrix.html" title="http://mathworld.wolfram.com/Matrix.html">Matrices</a> on Wolfram MathWorld</li> + <li><a class="external" href="http://en.wikipedia.org/wiki/Matrix_(mathematics)" title="http://en.wikipedia.org/wiki/Matrix_(mathematics)">Matrix</a> on Wikipedia</li> +</ul> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html new file mode 100644 index 0000000000..c8dfcc893d --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html @@ -0,0 +1,125 @@ +--- +title: WebGL을 사용한 객체 애니메이션 +slug: Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL +tags: + - WebGL + - 애니메이션 + - 회전 +translation_of: Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL", "Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL") }}</p> + +<p>앞 단원에서의 예제 코드는 정지되어 있는 것처럼 보이지만 사실은 15밀리초마다 한 번 씩 WebGL 장면(Scene)을 다시 그리도록 설정되어 있습니다. 그려질 객체의 상태가 계속 똑같기 때문에, 똑같은 객체를 15밀리초마다 계속 다시 그리고 있는 것입니다. 이제 우리가 그린 정사각형을 실제로 움직이게 만들어 보겠습니다.</p> + +<p>이번 예제에서는 2차원 정사각형을 3차원으로 회전시키고 이동시켜 보겠습니다. 3차원을 이루는 X, Y, Z축 방향 모두를 중심으로 회전시켜 보면, 우리가 그린 것은 2차원 정사각형이지만 3차원 공간 안에 존재한다는 것을 실감할 수 있을 것 입니다.</p> + +<h2 id="정사각형_회전시키기">정사각형 회전시키기</h2> + +<p>먼저 정사각형을 회전시켜 보겠습니다. 회전 시키려면 회전 상태(회전량)를 저장할 변수가 필요합니다:</p> + +<pre class="brush: js">var squareRotation = 0.0; +</pre> + +<p>이제 <span style="line-height: 16.7999992370605px;">정사각형을 그릴 때 회전을 반영하도록 </span><code>drawScene()</code> 함수를 수정해야 합니다. 정사각형을 처음에 그려질 위치로 이동시키고 나서 다음과 같이 회전을 적용합니다:</p> + +<pre class="brush: js">mvPushMatrix(); +mvRotate(squareRotation, [1, 0, 1]); +</pre> + +<p>현재 상태의 모델-뷰 행렬을 저장하고, X축과 Z축을 기준으로 <code>squareRotation</code> 만큼 행렬을 회전시킵니다.</p> + +<p>정사각형을 그리고 난 후에 모델-뷰 행렬을 원상태로 복구합니다:</p> + +<pre class="brush: js">mvPopMatrix(); +</pre> + +<p>모델-뷰 행렬을 복구하는 이유는 이 회전이 다른 객체에 영향을 미치는 것을 예방하기 위해서 입니다.</p> + +<p>실제로 애니메이션 효과가 나타나도록 하려면 <code>squareRotation</code> 값을 시간이 지남에 따라 계속 변경해주는 코드를 추가해야 합니다. <code>lastSquareUpdateTime</code>이라는 변수에 마지막으로 다시 그렸던 시각을 저장하고, 다음과 같은 코드를 <code><span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">drawScene()</span></code>에 추가합니다: </p> + +<pre class="brush: js">var currentTime = (new Date).getTime(); +if (lastSquareUpdateTime) { + var delta = currentTime - lastSquareUpdateTime; + + squareRotation += (30 * delta) / 1000.0; +} + +lastSquareUpdateTime = currentTime; +</pre> + +<p>이 코드는 마지막으로 <code>squareRotation</code>의 값을 변경한 시각과 현재 시각과의 차이를 이용해서 회전량을 나타내는 <code>squareRotation</code>의 값을 결정 합니다.</p> + +<h2 id="정사각형_이동시키기">정사각형 이동시키기</h2> + +<p>정사각형을 그리기 전에 위치값을 변경하면 정사각형을 이동시킬 수 있습니다. 이번 예제에서는 학습을 목적으로 아주 기초적인 애니메이션을 구현해봅니다. 실전에서 이런 식으로 애니메이션을 구현하면 사람들에게 사랑받지 못할 것입니다.</p> + +<p>X, Y, Z 각 축별 오프셋(offset) 값을 새 변수에 저장합니다:</p> + +<pre class="brush: js">var squareXOffset = 0.0; +var squareYOffset = 0.0; +var squareZOffset = 0.0; +</pre> + +<p>축 별 위치 변동값을 다음과 같이 각기 다른 값으로 지정합니다:</p> + +<pre class="brush: js">var xIncValue = 0.2; +var yIncValue = -0.4; +var zIncValue = 0.3; +</pre> + +<p>이제 위치 변동값을 계산하는 코드를 위에서 구현한 회전량 계산 코드 바로 아래에 추가합니다:</p> + +<pre class="brush: js">squareXOffset += xIncValue * ((30 * delta) / 1000.0); +squareYOffset += yIncValue * ((30 * delta) / 1000.0); +squareZOffset += zIncValue * ((30 * delta) / 1000.0); + +if (Math.abs(squareYOffset) > 2.5) { + xIncValue = -xIncValue; + yIncValue = -yIncValue; + zIncValue = -zIncValue; +} +</pre> + +<p>마지막으로 다음 코드를 <code>drawScene()</code> 함수에 추가합니다:</p> + +<pre class="brush: js">mvTranslate([squareXOffset, squareYOffset, squareZOffset]);</pre> + +<p>이제 정사각형이 화면에서 좌, 우, 위, 아래, 앞, 뒤로 완전히 자기멋대로 움직이면서 회전하는 것을 볼 수 있습니다. 어찌보면 바탕화면 보호기 같기도 합니다.</p> + +<p>WebGL이 지원되는 브라우저라면, <a href="/samples/webgl/sample4/index.html">여기</a>에서 실제 동작하는 예제를 확인할 수 있습니다.</p> + +<h2 id="추가적인_행렬_연산">추가적인 행렬 연산</h2> + +<p>아래의 예제는 스택을 사용하는 두 개의 push, pop 루틴과, 주어진 각도만큼 회전시키는 행렬을 포함하는 몇 가지 추가적인 행렬 연산을 사용합니다. 한 번 참고하시기 바랍니다:</p> + +<pre class="brush: js">var mvMatrixStack = []; + +function mvPushMatrix(m) { + if (m) { + mvMatrixStack.push(m.dup()); + mvMatrix = m.dup(); + } else { + mvMatrixStack.push(mvMatrix.dup()); + } +} + +function mvPopMatrix() { + if (!mvMatrixStack.length) { + throw("Can't pop from an empty matrix stack."); + } + + mvMatrix = mvMatrixStack.pop(); + return mvMatrix; +} + +function mvRotate(angle, v) { + var inRadians = angle * Math.PI / 180.0; + + var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4(); + multMatrix(m); +} +</pre> + +<p>이 루틴은 예전에 <span style="line-height: 16.7999992370605px;">Vlad Vukićević가 작성했던 예제를 참고하여 만들었습니다.</span></p> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL", "Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL") }}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html new file mode 100644 index 0000000000..723c1318ab --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html @@ -0,0 +1,117 @@ +--- +title: WebGL에서의 텍스쳐 애니메이션 +slug: Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL +tags: + - Animation + - HTML5 + - Texture + - Video + - WebGL + - 애니메이션 + - 웹지엘 + - 텍스쳐 +translation_of: Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL +--- +<p>{{WebGLSidebar("Tutorial") }} {{Previous("Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}</p> + +<p>앞 단원에서는 정적인 텍스쳐를 사용한 예제를 만들어 봤었는데, 이번에는 Ogg 비디오 파일을 이용해서 텍스쳐에 애니메이션 효과를 적용해 보겠습니다. 사실 만들기 상당히 쉽지만, 그래도 보는 재미는 쏠쏠하니까 한 번 만들어 보겠습니다. 텍스쳐를 구성할 소스로 어떤 종류의 데이터(예를 들면 <span style="line-height: 16.7999992370605px;">{{ HTMLElement("canvas") }}와 같은</span>)를 쓰더라도 코드는 비슷할 것입니다.</p> + +<h2 id="텍스쳐를_구성할_비디오_로딩">텍스쳐를 구성할 비디오 로딩</h2> + +<p>가장 먼저 할 일은 비디오 프레임을 조회하는데 사용할 <span style="line-height: 16.7999992370605px;">{{ HTMLElement("video") }} </span>요소를 생성하는 것입니다:</p> + +<pre class="brush: js"><video id="video"> + Your browser doesn't appear to support the HTML5 <code>&lt;video&gt;</code> element. +</video> +</pre> + +<blockquote> +<p>역자 주 : 실제 예제 소스 코드를 보면 비디오 태그가 위와 같이 id 속성만 있는 것이 아니라 아래와 같이 src 속성과 autoplay 속성도 추가되어 있습니다.</p> + +<pre class="brush: js" style="font-size: 14px;"><video id="video" src="Firefox.ogv" autoplay> + Your browser doesn't appear to support the HTML5 <code>&lt;video&gt;</code> element. +</video> +</pre> +</blockquote> + +<p>위 코드는 <span style="line-height: 16.7999992370605px;">'Firefox.ogv" 비디오 파일을 재생할 </span><span style="line-height: 16.7999992370605px;">{{ HTMLElement("video") }} 요소를 생성합니다. 다음과 같은 CSS 코드를 작성해서 비디오가 자동으로 표시되지 않도록 합니다:</span></p> + +<pre class="brush: css">video { + display: none; +} +</pre> + +<p>이제 자바스크립트 코드를 살펴보겠습니다. <code>start()</code> 함수에 비디오 요소에 대한 참조를 가져오는 코드를 추가합니다:</p> + +<pre class="brush: js">videoElement = document.getElementById("video"); +</pre> + +<p><code>setInterval()</code>을 이용해서 <code>drawScene()</code>을 주기적으로 호출하던 코드를 아래의 코드로 대체합니다:</p> + +<pre class="brush: js">videoElement.addEventListener("canplaythrough", startVideo, true); +videoElement.addEventListener("ended", videoDone, true); +</pre> + +<p><span style="line-height: 16.7999992370605px;">비디오 요소의 src 속성으로 </span>비디오 파일의 위치를 지정해서 비디오 로딩을 시작합니다. FIXME (이 글의 작성자의 한 사람인 bjacob에게) : <code>preload="auto"</code>를 여기에서 명시하지 않으면 파이어폭스에서는 <code>canplaythrough </code>이벤트가 발생되지 않음. 크롬에서는 <code>preload="auto"</code> 지정 여부와 관계없이 비디오 로딩 시작.</p> + +<blockquote> +<p>역자 주 : 예제에 보면 아래와 같이 자바스크립트에서 preload나 src를 명시하지 않고, 위의 역자 주에 있는 것처럼 비디오 태그 내에 속성값으로 기술하고 있으므로, 바로 위 문단과 아래의 코드는 무시해도 좋을 것 같습니다.</p> +</blockquote> + +<pre>video.preload = "auto"; +videoElement.src = "Firefox.ogv";</pre> + +<p>비디오 재생 시 끊김이 없도록 충분히 버퍼링 해둔 후에 애니메이션을 시작하는 것이 좋습니다. 전체 비디오가 끊김 없이 재생될 수 있을만큼 충분한 데이터가 버퍼링 된 후에 비디오의 재생이 시작되도록 <span style="line-height: 16.7999992370605px;">아래와 같이 </span><span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">canplaythrough </span><span style="line-height: 16.7999992370605px;">이벤트에 대한 리스너인 <code>startVideo()</code>를 추가합니다:</span></p> + +<pre class="brush: js">function startVideo() { + videoElement.play(); + intervalID = setInterval(drawScene, 15); +} +</pre> + +<p>위 코드는 단순히 비디오 재생을 시작하고, 정육면체의 렌더링을 처리하는 <code>drawScene()</code> 함수를 <span style="line-height: 16.7999992370605px;"><code>setInterval()</code> 함수를 이용해서 주기적으로 호출합니다.</span></p> + +<p>비디오가 끝날 때 방생하는 <code>ended </code>이벤트에 대한 리스너도 추가해서, 비디오 재생이 끝나면 불필요하게 CPU 시간을 잡아먹지 않도록 애니메이션을 중단시킵니다.</p> + +<pre class="brush: js">function videoDone() { + clearInterval(intervalID); +}</pre> + +<p><code>videoDone()</code> 함수는 단순히 <span style="line-height: 16.7999992370605px;">{{ domxref("window.clearInterval()") }} 함수를 호출해서 애니메이션을 업데이트하는 <code>drawScene()</code> 함수의 호출을 중단시킵니다.</span></p> + +<h2 id="비디오_프레임을_텍스쳐로_사용하기">비디오 프레임을 텍스쳐로 사용하기</h2> + +<p>비디오 로딩과 재생에 대한 처리를 마치면, <code>initTexture()</code> 함수의 내용을 변경해야 합니다. 이번에는 이미지를 로딩하는 대신 비어있는 텍스쳐 객체를 생성하고, 텍스쳐 객체를 나중에 사용할 수 있도록 필터링을 설정하기만 하면 되므로, <code>initTexture()</code> 함수의 내용이 앞 단원의 예제보다 <span style="line-height: 16.7999992370605px;">조금 </span>더 간단해집니다: </p> + +<pre class="brush: js">function initTextures() { + cubeTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, cubeTexture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); +} +</pre> + +<p>가장 중요한 텍스쳐의 업데이트를 담당하는 <code>updateTexture()</code> 함수는 다음과 같습니다:</p> + +<pre class="brush: js">function updateTexture() { + gl.bindTexture(gl.TEXTURE_2D, cubeTexture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, videoElement); +} +</pre> + +<p>위와 같은 코드를 전에 본 적이 있을 것입니다. <code>texImage2D()</code>를 호출할 때 <code>Image </code>객체를 전달하지 않고 <span style="line-height: 16.7999992370605px;">{{ HTMLElement("video") }} 요소를 전달한다는 것만 제외하면, 앞 단원에서 사용했던 </span><span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;"><code>handleTextureLoaded()</code> </span><span style="line-height: 16.7999992370605px;">루틴과 거의 똑같습니다. 현재의 프레임을 추출해서 텍스쳐로 사용하는 것은 WebGL이 알아서 처리합니다.</span></p> + +<p><span style="line-height: 16.7999992370605px;"><code>drawScene() </code>함수에는 맨 앞에 </span><span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">updateTexture()</span><span style="line-height: 16.7999992370605px;">를 추가합니다. <code>drawScene()</code> 함수에 의해 장면을 다시 그릴 때마다 </span><code style="font-style: normal; line-height: 16.7999992370605px;">updateTexture()</code>이 호출됩니다.</p> + +<p>이제 완성입니다! WebGL을 지원하는 브라우저라면 <a href="/samples/webgl/sample8/index.html">여기</a>에서 실제 작동하는 예제를 확인할 수 있습니다.</p> + +<h2 id="참고_자료">참고 자료</h2> + +<ul> + <li><a href="/ko/docs/Web/Guide/HTML/Using_HTML5_audio_and_video">파이어폭스에서 audio와 video 사용하기</a></li> +</ul> + +<p>{{Previous("Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html new file mode 100644 index 0000000000..e4d3cf991f --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html @@ -0,0 +1,132 @@ +--- +title: WebGL로 3D 객체 만들기 +slug: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL +tags: + - 3D + - 3차원 + - WebGL + - 입체 +translation_of: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}</p> + +<p>이제 우리가 만든 정사각형에 5개의 면을 더해서 3차원 정육면체를 만들어 보겠습니다. 이 작업을 조금 더 효율적으로 하기 위해서 <code>drawArray()</code> 메서드를 호출해서 정점을 직접 핸들링하는 대신에, 정점 배열을 인덱스와 값으로 정의된 테이블이라고 생각하고, 각 정점을 인덱스로 참조해서 정육면체 각 면의 정점 위치를 정의하고 <code>gl.drawElements()</code>를 호출해서 그려보겠습니다.</p> + +<p>고려 사항 : 정육면체의 각 면은 4개의 정점이 필요하고, 정육면체에는 6개의 면이 있으므로 총 24개의 정점이 필요할 것 같지만, 하나의 정점이 세 개의 면에 공통적으로 사용되므로 실제로는 8개의 정점만 있으면 됩니다. 그리고 이 8개의 정점 각각에 인덱스 번호를 매겨서 참조하면 한 개의 정점을 세 개의 면에 재사용할 수 있습니다. 하지만 이번 예제에서는 8개가 아니라 24개의 정점을 사용하는데, 그 이유는 한 꼭지점에서 만나는 세 개의 면마다 다른 색상을 적용할 것이기 때문입니다. 하나의 정점은 한 개의 색상만을 가질 수 있으므로, 세 개의 색상을 표시하려면 세 개의 정점이 필요합니다. 따라서 <span style="line-height: 16.7999992370605px;">기하학적으로는 하나의 꼭지점일지라도 </span>세 개의 색상을 표시하기 위해서는 세 개의 정점이 필요 합니다.</p> + +<h2 id="정육면체의_정점_위치_정의">정육면체의 정점 위치 정의</h2> + +<p>먼저 <code>initBuffers()</code> 내부에 있는 코드를 수정해서 정육면체의 정점 버퍼를 만듭니다. 방식은 정사각형을 그릴 때와 거의 비슷하지만, 정점의 수는 하나의 면에 4개 씩, 총 24개로 정사각형보다 더 많습니다:</p> + +<pre class="brush: js">var vertices = [ + // 앞면(Front face) + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + + // 뒤면(Back face) + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + // 위면(Top face) + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + + // 아래면(Bottom face) + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + // 오른쪽면(Right face) + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + + // 왼쪽면(Left face) + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0 +]; +</pre> + +<h2 id="정점의_색상_정의">정점의 색상 정의</h2> + +<p>24개 정점의 색상 배열도 만들어야 합니다. 각 면의 색상을 하나의 배열로 정의하고, 반복문을 돌면서 모든 정점의 색상 정보를 하나의 배열로 만듭니다.</p> + +<pre class="brush: js">var colors = [ + [1.0, 1.0, 1.0, 1.0], // 앞면 : 흰색 + [1.0, 0.0, 0.0, 1.0], // 뒤면 : 빨간색 + [0.0, 1.0, 0.0, 1.0], // 위면 : 녹색 + [0.0, 0.0, 1.0, 1.0], // 아래면 : 파란색 + [1.0, 1.0, 0.0, 1.0], // 오른쪽면 : 노란색 + [1.0, 0.0, 1.0, 1.0] // 왼쪽면 : 보라색 +]; + +var generatedColors = []; + +for (j=0; j<6; j++) { + var c = colors[j]; + + for (var i=0; i<4; i++) { + generatedColors = generatedColors.concat(c); + } +} + +cubeVerticesColorBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer); +gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW); +</pre> + +<h2 id="인덱스_배열_정의">인덱스 배열 정의</h2> + +<p>정점 배열을 만들었으면 인덱스 배열(원문 : element array)을 만들어야 합니다.</p> + +<pre class="brush: js">cubeVerticesIndexBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer); + +// 인덱스 배열은 하나의 면을 두 개의 삼각형으로 정의합니다. +// 인덱스 배열의 원소인 각 숫자는 정점 배열에서 한 정점의 위치를 나타냅니다. +// 즉, 아래의 인덱스 배열에서의 0, 1, 2, 0, 2, 3은 +// 정점 배열에서 0, 1, 2번째의 정점으로 이루어진 삼각형과 +// 0, 2, 3번째 정점으로 이루어진 삼각형 두 개로 +// 하나의 면을 나타낸다는 의미입니다. + +var cubeVertexIndices = [ + 0, 1, 2, 0, 2, 3, // front + 4, 5, 6, 4, 6, 7, // back + 8, 9, 10, 8, 10, 11, // top + 12, 13, 14, 12, 14, 15, // bottom + 16, 17, 18, 16, 18, 19, // right + 20, 21, 22, 20, 22, 23 // left +]; + +// 인덱스 배열을 GL에 전달 + +gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW); +</pre> + +<p><code>cubeVertexIndices</code> 배열은 <span style="line-height: 16.7999992370605px;">정육면체 정점 배열의 인덱스값을 원소로 가지며, 각 인덱스 값에 해당하는 정점을 순서대로 세 개씩 묶어서 하나의 삼각형을 구성하고, 삼각형 두 개를 순서대로 묶어서 하나의 면으로 정의합니다. 따라서 6개의 면을 가진 정육면체는 12개의 삼각형의 조합으로 표현할 수 있습니다.</span></p> + +<h2 id="정육면체_그리기">정육면체 그리기</h2> + +<p>다음 단계로 정육면체의 인덱스 버퍼를 이용해서 정육면체를 그릴 수 있도록 <code>drawScene()</code> 함수 내부에 코드를 추가 합니다. 인덱스 버퍼를 사용하기 위한 <code>bindBuffer()</code>와 정육면체를 그리기 위한 <code>drawElements()</code> 호출문을 추가합니다:</p> + +<pre class="brush: js">gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer); +setMatrixUniforms(); +gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0); +</pre> + +<p>정육면체의 각 면이 두 개의 삼각형으로 이루어져 있으므로, 한 면에는 6개의 정점이 있으며, 정육면체 전체로는 총 36개의 정점이 존재합니다. 정점 배열에는 24개의 정점이 있었으므로 36개의 정점을 구성하려면 하나의 정점이 여러번 중복되어 사용 되었을 것 입니다. 비효율적이라고 생각될 수도 있지만, 인덱스 배열은 처리가 단순한 정수형 데이터로만 구성되어 있으므로, 36개의 정수형 배열이 하나의 애니메이션 프레임에서 처리하기에 지나치게 많은 수준의 데이터는 아닙니다.</p> + +<p>이제 지금까지 만든 정육면체를 확인 해 보겠습니다. WebGL을 지원하는 브라우저에서는 <a href="/samples/webgl/sample5/index.html">여기</a>에서 6개의 면이 원색으로 채색된 정육면체를 볼 수 있습니다.</p> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html new file mode 100644 index 0000000000..95fcd36346 --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html @@ -0,0 +1,100 @@ +--- +title: Getting started with WebGL +slug: Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{Next("Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context")}}</p> + +<p><a class="external" href="http://www.khronos.org/webgl/" title="http://www.khronos.org/webgl/">WebGL</a>은 플러그인을 사용하지 않고 <a class="external" href="http://www.khronos.org/opengles/" title="http://www.khronos.org/opengles/">OpenGL ES</a> 2.0 기반 API를 이용하여 브라우저의 HTML <a class="internal" href="/en/HTML/Canvas" title="en/HTML/Canvas"><code>canvas</code></a>에 렌더링하여 3D 웹 콘텐츠 제작을 가능하게 합니다. WebGL 프로그램은 컴퓨터의 그래픽 처리 장치(GPU)에서 실행되는 자바스크립트나 특수 효과(셰이더 코드)코드로 구성됩니다. WebGL 요소들은 다른 HTML 요소들과 혼합될 수 있고 페이지나 페이지 배경의 다른 부분과 합성될 수 있습니다.</p> + +<p>이 문서는 기본 WebGL 기본 사항을 소개합니다. 이 문서에서는 3D 그래픽에 관련된 수학적 이해를 이미 이해하고 있다고 간주하고 OpenGL 자체에 대하여 설명하지 않을 것입니다.</p> + +<h2 id="3D_렌더링_준비" style="line-height: 30px; font-size: 2.14285714285714rem;">3D 렌더링 준비</h2> + +<p>WebGL을 사용하여 3D 렌더링을 하는 데 첫 번째로 필요한 것은 캔버스입니다. <span style="line-height: 1.5;">아래 HTML 코드는 canvas를 만들고 사용할 WebGL 컨텍스트를 초기화하는 onload 이벤트 핸들러를 지정합니다.</span></p> + +<pre class="brush: html"><body onload="start()"> + <canvas id="glcanvas" width="640" height="480"> + Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element. + </canvas> +</body></pre> + +<h3 id="WebGL_컨텍스트_준비" style="line-height: 24px; font-size: 1.71428571428571rem;">WebGL 컨텍스트 준비</h3> + +<p>자바스크립트 코드에서 <code>start()</code> 함수는 문서가 다 불러와지면 호출됩니다. 이 함수의 기능은 WebGL 컨텍스트를 설정하고 콘텐츠 렌더링을 시작하는 것입니다.</p> + +<pre class="brush: js">var gl; // A global variable for the WebGL context + +function start() { + var canvas = document.getElementById("glcanvas"); + + gl = initWebGL(canvas); // Initialize the GL context + + // Only continue if WebGL is available and working + + if (gl) { + gl.clearColor(0.0, 0.0, 0.0, 1.0); // Set clear color to black, fully opaque + gl.enable(gl.DEPTH_TEST); // Enable depth testing + gl.depthFunc(gl.LEQUAL); // Near things obscure far things + gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); // Clear the color as well as the depth buffer. + } +} </pre> + +<p><span style="line-height: 1.5;">첫 번째 할 일은 canvas에 대한 참조를 얻는 것 입니다. canvas라는 변수에 지정합니다.</span> 당연히 canvas를 반복적으로 참조할 필요는 없고 전역 변수로 저장하는 것은 피해야 합니다. 지역 변수나 객체의 필드 멤버로 참조해야 됩니다.</p> + +<p>캔버스가 있으면 <code>initWebGL()</code>이라는 함수를 호출할 수 있습니다. 이 함수는 일시적으로 정의되고 WebGL 컨텍스트를 초기화하는 일을 합니다.</p> + +<p><span style="line-height: 1.5;">만약 컨텍스트가 성공적으로 초기화 되면 gl은 이를 참조합니다. </span><span style="line-height: 1.5;">이번 예제에서는 검은색 투명 색상을 설정하면 컨텍스트를 그 색상으로 지정합니다. </span>그 다음 컨텍스트는 설정 매개변수로 설정됩니다. 예제에서는 깊이 테스트가 가능하고 가까운 물체가 멀리 떨어저 있는 물체를 가리는 것을 지정합니다.</p> + +<p>코드에서 초기화를 전달하는 목적은 우리가 하려는 것 전부 입니다. 잠시 후 실제로 무언가를 어떻게 시작하는가 알아볼 것입니다.</p> + +<h3 id="WebGL_컨텍스트_생성">WebGL 컨텍스트 생성</h3> + +<p><code>initWebGL()</code> 함수는 다음과 같습니다.</p> + +<pre class="brush: js">function initWebGL(canvas) { + gl = null; + + try { + // Try to grab the standard context. If it fails, fallback to experimental. + gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + } + catch(e) {} + + // If we don't have a GL context, give up now + if (!gl) { + alert("Unable to initialize WebGL. Your browser may not support it."); + gl = null; + } + + return gl; +} +</pre> + +<p>캔버스에서 WebGL 컨텍스트를 얻기 위해 canvas로 "webgl"이라고 불리는 컨텍스트를 요청할 것입니다. 만약에 실패한다면 "experimental-webgl"이라는 이름으로 시도할 것입니다. 만약 이마저도 실패한다면 사용자에게 사용 중인 브라우저가 WebGL을 지원하지 않는다는 경고를 출력할 것입니다. <span style="line-height: 1.5;">이게 전부입니다. 이 시점에서 gl은 null(WebGL 컨텍스트를 이용할 수 없다는 의미)이거나 렌더링할 WebGL 컨텍스트를 참조할 것입니다.</span></p> + +<div class="note"><strong>Note:</strong> experimental-webgl이라는 이름은 사양 개발 시 사용되는 컨텍스트를 위한 일시적인 이름입니다. webgl은 사양이 확정되면 사용됩니다.</div> + +<p>이 시점에서 이 코드는 WebGL 컨텍스트가 성공적으로 초기화하는 데 충분한 코드입니다. 이 코드를 통해 검정 박스 형태의 빈 공간이 형성되며, 여기에<span style="line-height: 1.5;"> 콘텐츠를 생성할 기본 준비가 되었습니다.</span></p> + +<p><a href="/samples/webgl/sample1/index.html" title="https://developer.mozilla.org/samples/webgl/sample1/index.html">여기를 클릭하여 예제를 확인하세요</a>. 브라우저가 WebGL을 호환한다면 실행될 것 입니다.</p> + +<h3 id="WebGL_컨텍스트_크기_조정">WebGL 컨텍스트 크기 조정</h3> + +<p>이제 새로운 WebGL 컨텍스트는 새로운 컨텍스트 인스턴스를 얻었습니다. 그리고 CSS 없이 캔버스 요소의 height와 width로 뷰포트의 해상도를 설정합니다. 캔버스 요소의 스타일 편집하면 출력되는 크기를 변경될 것이지만 렌더링 해상도는 변경되지 않습니다. 또한 컨텍스트가 생성된 후 캔버스 요소의 width와 height 속성을 편집하면 그려지는 픽셀 수를 변경할 수 없습니다. <span style="line-height: 1.5;">WebGL 렌더의 해상도를 변경하려면 사용자가 캔버스 문서 전체 창 크기를 조정하거나 앱에서 그래픽 설정을 조정할 수 있게 하길 원할 것입니다. WebGL 컨텍스트 viewport() 함수가 변경할 수있는 것으로 알려져 있습니다.</span></p> + +<p>렌더링된 WebGL 컨텍스트의 해상도를 수정하려면 위에 나오는 gl과 canvas 변수를 사용해야 됩니다.</p> + +<pre class="brush: js">gl.viewport(0, 0, canvas.width, canvas.height);</pre> + +<p>캔버스는 CSS 스타일과 다른 해상도로 렌더링되어질 때 화면에서 차지하는 크기를 볼 것입니다. CSS로 크기를 조정하면 낮은 해상도에서 렌더링하거나 브라우저 화면을 확대할 때 자원을 절약하는 데 유용합니다. 축소는 슈퍼샘플 안티에일리어싱(SSAA) 효과를 사용할 때 가능합니다. (많은 성능 비용이 발생하고 작은 결과이기는 하지만) <span style="line-height: 1.5;">아래에 참고 사항을 살펴보는 것이 가장 좋습니다.</span></p> + +<ul> + <li><a href="http://dev.opera.com/articles/view/an-introduction-to-webgl/">WebGL에 대한 소개</a> - DEV.OPERA에 있는 Luz Caballero가 작성한 문서입니다. 이 문서는 WebGL이 무엇인지, 어떻게 작동되는지, 렌더링 파이프라인 개념에 대하여 설명하고 몇 가지 WebGL 라이브러리에 대하여 소개합니다.</li> + <li><a href="http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Table-of-Contents.html">현대 OpenGL에 대한 소개</a> - Joe Groff가 작성한 OpenGL에 대한 좋은 문서 시리즈입니다. 조는 명확하게 역사를 가지고 OpenGL의 중요한 그래픽 파이프라인 개념에 대해 소개하고 몇 가지 데모를 통해 OpenGL이 어떻게 작동되는지 설명하기 위해 예제들을 제공합니다. 만약 OpenGL에 대한 개념이 안 잡혀 있다면 시작하기 좋은 장소가 될 것입니다.</li> +</ul> + +<p>{{Next("Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/index.html b/files/ko/web/api/webgl_api/tutorial/index.html new file mode 100644 index 0000000000..0e6230b0ea --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/index.html @@ -0,0 +1,40 @@ +--- +title: WebGL tutorial +slug: Web/API/WebGL_API/Tutorial +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial +--- +<div>{{WebGLSidebar}}</div> + +<div class="summary"> +<p><a class="external" href="http://www.khronos.org/webgl/" title="http://www.khronos.org/webgl/">WebGL</a> 은 WebGL을 지원하는 브라우져에서 plugin을 사용하지 않고도, 웹 콘텐츠가 <a class="external" href="http://www.khronos.org/opengles/" title="http://www.khronos.org/opengles/">OpenGL ES</a> 2.0 기반의 API를 이용하여 HTML {{HTMLElement("canvas")}}에서 3D 랜더링을 할 수 있도록 해 줍니다. WebGL 프로그램은 JavaScripts로 작성 된 제어 코드와 컴퓨터의 Graphics Processing Unit (GPU)에서 실행되는 특수한 효과를 내는 코드(Shader code)로 구성 됩니다. WebGL 요소들은 다른 HTML요소들과 섞어서 함께 사용 할 수 있으며 페이지의 다른 부분이나 페이지 배경과 함께 사용 할 수 있습니다.</p> +</div> + +<p><span class="seoSummary">이 튜토리얼은 WebGL 그래픽을 그리기 위해 <canvas>요소를 어떻게 사용하는지에 관해 기본부터 기술합니다. 제공된 예제들은 여러분이 WebGL로 무엇을 할 수 있는지를 명확히하고, 여러분 소유의 콘텐츠를 제작할 수 있도록 작은 코드들을 제공 할 것입니다.</span></p> + +<h2 id="시작하기_전에">시작하기 전에</h2> + +<p><code><canvas></code> 요소를 사용하는 것은 크게 어렵진 않지만, 여러분은 <a href="/en-US/docs/Web/HTML" title="HTML">HTML</a> 과 <a href="/en-US/docs/Web/JavaScript" title="JavaScript">JavaScript</a>에 대하여 기본적인 이해가 꼭 필요합니다. <code><canvas></code> 요소와 WebGL은 일부 오래된 브라우저에서 지원되지 않으나, 최근 버전의 모든 주요 브라우저에서 지원됩니다. 우리는 canvas에 그림을 그리기 위해 그림을 신속하게 생성하는 JavaScript 콘텍스트 객체를 사용합니다.</p> + +<h2 id="In_this_tutorial">In this tutorial</h2> + +<dl> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL">Getting started with WebGL</a></dt> + <dd>How to set up a WebGL context.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context">Adding 2D content to a WebGL context</a></dt> + <dd>How to render simple flat shapes using WebGL.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL">Using shaders to apply color in WebGL</a></dt> + <dd>Demonstrates how to add color to shapes using shaders.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL">Animating objects with WebGL</a></dt> + <dd>Shows how to rotate and translate objects to create simple animations.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL">Creating 3D objects using WebGL</a></dt> + <dd>Shows how to create and animate a 3D object (in this case, a cube).</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL">Using textures in WebGL</a></dt> + <dd>Demonstrates how to map textures onto the faces of an object.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Lighting_in_WebGL">Lighting in WebGL</a></dt> + <dd>How to simulate lighting effects in your WebGL context.</dd> + <dt><a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL">Animating textures in WebGL</a></dt> + <dd>Shows how to animate textures; in this case, by mapping an Ogg video onto the faces of a rotating cube.</dd> +</dl> diff --git a/files/ko/web/api/webgl_api/tutorial/lighting_in_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/lighting_in_webgl/index.html new file mode 100644 index 0000000000..37eb1ee7fa --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/lighting_in_webgl/index.html @@ -0,0 +1,177 @@ +--- +title: WebGL에서 조명 효과 적용하기 +slug: Web/API/WebGL_API/Tutorial/Lighting_in_WebGL +tags: + - WebGL + - 방향광 + - 빛 + - 웹지엘 + - 점광 + - 조명 + - 주변광 +translation_of: Web/API/WebGL_API/Tutorial/Lighting_in_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL", "Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL")}}</p> + +<p>WebGL은 OpenGL 표준과는 다르게 자체적인 조명 효과를 제공하지 않습니다. 따라서 WebGL에서의 조명 효과는 개발자 스스로 만들어야 합니다. 다행스럽게도 조명 효과를 만드는 것이 아주 어려운 일은 아니며, 이 글을 통해 몇 가지 기초적인 부분을 이해할 수 있을 것입니다.</p> + +<h2 id="3D에서의_조명_시뮬레이션과_명암_효과">3D에서의 조명 시뮬레이션과 명암 효과</h2> + +<p>3D 그래픽의 조명 시뮬레이션에 대한 이론적 바탕에 대해 상세하게 알아보는 것은 이 글의 범위를 많이 벗어난다고 할 수 있지만, 그 동작 원리에 대해서는 조금이나마 알아볼 필요가 있습니다. 먼저 가장 널리 사용되는 조명 모델인 <a href="http://en.wikipedia.org/wiki/Phong_shading">퐁 셰이딩(Phong shading)에 대한 위키피디아 자료</a>를 한 번 읽어보시기 바랍니다.</p> + +<p>조명에는 세 가지 기본 타입이 있습니다:</p> + +<p><strong>주변광(Ambient light)</strong>은 장면(scene) 전반에 걸쳐 스며드는 빛으로, 방향성이 없으며 장면 내에 있는 모든 표면을 그 표면의 방향과 관계없이 동일한 밝기로 비춰줍니다.</p> + +<p><strong>방향광(Directional light)</strong>은 특정한 방향으로만 비춰지는 빛입니다. 방향광은 아주 먼 곳에서 비춰지기 때문에 모든 빛 입자(photon, 광자)가 서로 평행한 방향으로 움직입니다. 방향광의 대표적인 예는 바로 태양광입니다.</p> + +<p><strong>점광(Point light)</strong>은 한 지점에서 모든 방향으로 퍼지면서 발산하는 빛입니다. 실생활에서 접할 수 있는 대부분의 빛이 이 점광에 해당합니다. 전구에서 나오는 빛이 점광의 대표적인 예라고 할 수 있겠습니다.</p> + +<p>이 글에서는 <a href="http://en.wikipedia.org/wiki/Specular_highlight">반사광 하이라이트(specular highlight)</a>나 점광원에 대해서는 다루지 않고, 단순한 방향광 조명과 주변광 조명만 알아 보겠습니다. 주변광에 방향광원(directional light source)을 더한 조명 효과를 <span style="line-height: 16.7999992370605px;"><a href="/en/WebGL/Using_textures_in_WebGL">앞 단원의 예제</a>에 있던 회전하는 정육면체에 적용해보겠습니다.</span></p> + +<p>점광원이나 반사광을 고려하지 않는다면, 방향광 조명을 구현하기 위한 정보는 크게 두 가지가 있습니다:</p> + +<ol> + <li>각 정점의 표면에 수직인 벡터를 의미하는 <strong>표면 법선 벡터(surface normal vector)</strong>.</li> + <li>빛이 쪼여지는 방향을 나타내는 <strong>방향 벡터</strong>.</li> +</ol> + +<p>위 두 가지 정보를 구하고나면, 방향광의 방향과 정육면체의 표면이 만나는 각도에 따라 달라지는 방향광 조명 효과와 모든 표면에 균일하게 적용되는 <span style="line-height: 16.7999992370605px;">주변광 조명 효과</span>를 반영해서 각 정점의 색상을 조정할 수 있도록 정점 셰이더를 수정해야 합니다. 셰이더 코드를 어떻게 수정하는지는 조금 이따가 살펴보기로 하고, 먼저 정점의 법선 벡터를 만드는 방법부터 알아보겠습니다.</p> + +<h2 id="정점_별_법선_구성">정점 별 법선 구성</h2> + +<p>조명 효과 적용을 위해 첫번째로 해야할 일은 정육면체를 이루는 모든 정점의 법선 배열을 구성하는 것입니다. 정육면체는 아주 단순한 객체이므로 정육면체의 법선 배열 역시 쉽게 만들 수 있습니다. 하지만 복잡하게 생긴 객체의 법선을 계산하는 것은 상당히 어렵습니다.</p> + +<pre class="brush: js">cubeVerticesNormalBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer); + +var vertexNormals = [ + // 앞 + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + // 뒤 + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + + // 위 + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + // 아래 + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + // 오른쪽 + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + + // 왼쪽 + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0 +]; + +gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertexNormals), gl.STATIC_DRAW); +</pre> + +<p>이런 배열의 처리는 앞 단원에서 여러 번 다뤄왔으므로 이젠 꽤 친숙해 보일 것입니다. 새로운 버퍼를 생성하고, 버퍼와 법선 배열을 바인딩하고, <code>bufferData()</code>를 호출해서 법선 배열을 버퍼에 전달합니다.</p> + +<p>그 다음에 법선 배열과 셰이더 attribute 변수에 바인딩해서 셰이더가 법선 배열 정보에 접근할 수 있도록 해주는 코드를 <span style="line-height: 16.7999992370605px;"><code>drawScene()</code>에 </span>추가합니다:</p> + +<pre class="brush: js">gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer); +gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); +</pre> + +<p>마지막으로 uniform 행렬을 구성하는 <code>setMatrixUniforms()</code>에 <strong>법선 행렬</strong>을 만들고 셰이더에게 전달하는 코드를 추가합니다. 법선 행렬은 광원을 기준으로 정육면체의 상대적인 방향에 따라 법선을 변환하는 데 사용됩니다:</p> + +<pre class="brush: js">var normalMatrix = mvMatrix.inverse(); +normalMatrix = normalMatrix.transpose(); +var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix"); +gl.uniformMatrix4fv(nUniform, false, new WebGLFloatArray(normalMatrix.flatten())); +</pre> + +<h2 id="셰이더_수정">셰이더 수정</h2> + +<p>조명 효과 구현을 위해 셰이더가 필요로 하는 데이터가 모두 준비되었으므로, 이제 셰이더 코드를 수정해보겠습니다.</p> + +<h3 id="정점_셰이더">정점 셰이더</h3> + +<p>제일 먼저 방향광 조명과 주변광 조명에 의한 각 정점의 명암 값을 계산해야 합니다. 일단 소스 코드부터 보겠습니다:</p> + +<pre class="brush: html"><script id="shader-vs" type="x-shader/x-vertex"> + attribute highp vec3 aVertexNormal; + attribute highp vec3 aVertexPosition; + attribute highp vec2 aTextureCoord; + + uniform highp mat4 uNormalMatrix; + uniform highp mat4 uMVMatrix; + uniform highp mat4 uPMatrix; + + varying highp vec2 vTextureCoord; + varying highp vec3 vLighting; + + void main(void) { + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); + vTextureCoord = aTextureCoord; + + // 조명 효과 적용 + + highp vec3 ambientLight = vec3(0.6, 0.6, 0.6); + highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75); + highp vec3 directionalVector = vec3(0.85, 0.8, 0.75); + + highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); + + highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); + vLighting = ambientLight + (directionalLightColor * directional); + } +</script> +</pre> + +<p>정점의 위치 계산이 끝나고, 정점의 텍셀(texel) 좌표값을 얻고나면, 그 값을 기준으로 정점의 명암을 계산할 수 있습니다.</p> + +<p>정점의 명암을 계산하려면 먼저 정육면체의 현재 위치와 방향을 기준으로 법선을 변환해야 합니다. 정점의 법선에 법선 행렬을 곱하면 <span style="line-height: 16.7999992370605px;">법선이 변환</span>됩니다. 그 다음에 변환된 법선과 방향 벡터(광원으로부터 빛이 비춰지는 방향)를 내적(dot product)하면 정점에 비춰지는 방향광의 양을 계산할 수 있습니다. 빛의 양이 음수일 수는 없으므로, 계산된 방향광의 양이 음수일 때는 방향광의 양을 0으로 설정해줍니다.</p> + +<p>방향광의 양을 계산하고 나면, 방향광의 색상과 방향광의 양을 곱한 값에 주변광의 값을 더해서 정점에 비춰지는 최종 빛의 양을 구할 수 있습니다. 결과적으로 <span style="line-height: 16.7999992370605px;">RGB값이 나오는데, 이 RGB값은 </span>프래그먼트 셰이더가 우리가 그릴 모든 픽셀에 대한 색상값을 계산하는데 사용됩니다.</p> + +<h3 id="프래그먼트_셰이더">프래그먼트 셰이더</h3> + +<p>이제 정점 셰이더에서 계산한 빛의 양을 반영해서 조명 효과를 표현할 수 있도록 <span style="line-height: 16.7999992370605px;">프래그먼트 셰이더를 </span>수정해야 합니다:</p> + +<pre class="brush: js"><script id="shader-fs" type="x-shader/x-fragment"> + varying highp vec2 vTextureCoord; + varying highp vec3 vLighting; + + uniform sampler2D uSampler; + + void main(void) { + mediump vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); + + gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a); + } +</script> +</pre> + +<p>앞 단원의 예제에서 했던 것처럼 텍셀의 색상값을 계산합니다. 하지만 <span style="line-height: 16.7999992370605px;">이번에는 </span>텍셀의 색상값을 바로 프래그먼트의 색상값으로 설정하지 않고, 조명 효과를 표현할 수 있도록 텍셀의 색상값에 빛의 양을 곱한 값을 프래그먼트의 색상값으로 설정합니다.</p> + +<p>자 이제 다 완성했습니다! WebGL을 지원하는 브라우저라면 <a href="/samples/webgl/sample7/index.html">여기</a>에서 실제 동작하는 예제를 확인할 수 있습니다.</p> + +<h2 id="연습해보기">연습해보기</h2> + +<p>알다시피 이번 예제는 정점 단위의 기본적인 조명 효과를 구현한 단순한 예제입니다. 더 수준 높은 컴퓨터 그래픽을 만들려면 정점 단위가 아니라 픽셀 단위의 조명 효과가 필요할 것입니다. 하지만 정점 단위의 조명 효과를 다룬 이 글이 고급 그래픽을 만드는데 도움이 될 것입니다.</p> + +<p>빛의 방향이나 광원의 색상 등을 다른 값으로 바꿔보는 등 다양하게 실험해보는 것도 조명 효과를 이해하는데 도움이 될 것입니다.</p> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL", "Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html new file mode 100644 index 0000000000..edfb74401a --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html @@ -0,0 +1,98 @@ +--- +title: WebGL에서 셰이더를 사용하여 색상 적용하기 +slug: Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL +tags: + - 색상 + - 셰이더 + - 웹지엘 + - 컬러 +translation_of: Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context", "Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL")}}</p> + +<p><a href="/ko/docs/Web/WebGL/Adding_2D_content_to_a_WebGL_context">앞 단원의 예제</a>에서 정사각형을 그려봤으니, 이제 정사각형에 색을 칠해 보겠습니다. 셰이더를 조금 수정하면 색을 칠할 수 있습니다.</p> + +<h2 id="정점에_색상_적용">정점에 색상 적용</h2> + +<p><span style="line-height: 16.7999992370605px;">GL(Graphic Library)에서 객체는 정점의 집합으로 구성되며, 각 정점은 위치값과 색상값을 가지고 있습니다. 기본적으로 정점이 아닌 모든 픽셀의 색상값은 선형 보간법(linear interpolation)을 이용해서 계산됩니다. 색상 뿐 아니라 위치를 포함한 다른 모든 속성들도 마찬가지로 선형 보간법으로 계산됩니다. 색상의 경우 선형 보간법을 통해 보간되면 자연스럽게 부드러운 그라데이션(gradation)이 형성됩니다. 앞 단원에서는 정점 셰이더에서 정점에 아무런 색상도 적용하지 않았습니다. 대신에 프래그먼트 셰이더에서 각 픽셀에 흰색을 적용했기 때문에 전체 사각형이 흰색으로 그려질 수 있었습니다.</span></p> + +<p>이제 정사각형의 각 꼭지점에 빨간색, 파란색, 녹색, 흰색을 적용해 보겠습니다. 정점이 아닌 픽셀들은 선형 보간에 의해 그라데이션이 형성됩니다. 먼저 네 개의 정점에 색을 지정하겠습니다. 정점에 색을 지정하려면 정점의 색상값을 가진 배열을 만들고, 이 배열을 WebGL 버퍼에 저장해야 합니다. <code>initBuffers()</code> 함수에 아래의 내용을 추가합니다:</p> + +<pre class="brush: js"> var colors = [ + 1.0, 1.0, 1.0, 1.0, // 흰색 + 1.0, 0.0, 0.0, 1.0, // 빨간색 + 0.0, 1.0, 0.0, 1.0, // 녹색 + 0.0, 0.0, 1.0, 1.0 // 파란색 + ]; + + squareVerticesColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); +} +</pre> + +<p>먼저 정점에 지정할 색상값을 가진 자바스크립트 배열을 생성합니다. 4개의 원소가 하나의 색을 나타내며, 정사각형의 4개의 꼭지점에 흰색, 빨간색, 파란색, 녹색이 지정되도록 값을 정합니다. 이 색상 정보 배열을 저장하기 위해 새로운 WebGL 버퍼를 생성하고, 배열은 부동소수점 형식으로 WebGL의 버퍼에 저장합니다.</p> + +<p>이 색상 정보를 실제 렌더링에 사용하려면, 컬러 버퍼에서 색상 정보를 읽을 수 있도록 정점 셰이더를 수정해야 합니다:</p> + +<pre class="brush: html"><script id="shader-vs" type="x-shader/x-vertex"> + attribute vec3 aVertexPosition; + attribute vec4 aVertexColor; + + uniform mat4 uMVMatrix; + uniform mat4 uPMatrix; + + varying lowp vec4 vColor; + + void main(void) { + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); + vColor = aVertexColor; + } +</script> +</pre> + +<p>수정 전의 소스와 가장 많이 달라진 점은, <span style="line-height: 16.7999992370605px;">색상 배열에서 읽은 값을 토대로 </span>각 정점에 색상값을 지정했다는 점입니다.</p> + +<h2 id="프래그먼트에_색상_입히기">프래그먼트에 색상 입히기</h2> + +<p>앞 단원에서 사용했던 프래그먼트 셰이더를 다시 한 번 보겠습니다:</p> + +<pre class="brush: html"><script id="shader-fs" type="x-shader/x-fragment"> + void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + } +</script> +</pre> + +<p>프래그먼트에 색상 입히기는 아주 쉽습니다. 위와 같이 프래그먼트 셰이더 내에서 흰색을 지정해주는 대신 단순히 <code>vColor</code> 변수에서 값을 읽어오도록 수정하기만 하면, 각 픽셀 별로 보간된 색상값을 구할 수 있습니다:</p> + +<pre class="brush: html"><script id="shader-fs" type="x-shader/x-fragment"> + varying lowp vec4 vColor; + + void main(void) { + gl_FragColor = vColor; + } +</script> +</pre> + +<p>위와 같이 각 프래그먼트는 정점을 기준으로 상대적인 위치에 따라 선형 보간된 색상값을 갖게 됩니다.</p> + +<h2 id="색상을_적용해서_그리기">색상을 적용해서 그리기</h2> + +<p>이제 셰이더 프로그램에서 색상 변수를 초기화하고 활성화하는 내용을 <code style="font-style: normal; line-height: 16.7999992370605px;">initShaders()</code><span style="line-height: 16.7999992370605px;">에 </span>추가합니다:</p> + +<pre class="brush: js">vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); +gl.enableVertexAttribArray(vertexColorAttribute); +</pre> + +<p>다음은 정사각형을 그릴 때 색상 정보를 사용하도록 <code>drawScene()</code>을 수정합니다:</p> + +<pre class="brush: js">gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer); +gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); +</pre> + +<p>이제 WebGL이 호환되는 브라우저에서 <a href="/samples/webgl/sample3/index.html">샘플</a>을 보면, 검정색 바탕에 아래와 같은 정사각형이 그려지는 것을 볼 수 있습니다:</p> + +<p><img alt="screenshot.png" class="default internal" src="/@api/deki/files/4081/=screenshot.png"></p> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context", "Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL")}}</p> diff --git a/files/ko/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html b/files/ko/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html new file mode 100644 index 0000000000..b84b49017c --- /dev/null +++ b/files/ko/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html @@ -0,0 +1,183 @@ +--- +title: WebGL에서 텍스쳐 사용하기 +slug: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL +tags: + - Texture + - WebGL + - 텍스쳐 +translation_of: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL +--- +<p>{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}</p> + +<p>앞 단원의 예제에서 회전하는 3차원 정육면체를 만들어봤습니다. 이번에는 정육면체의 각 면에 단색으로 색을 칠하는 대신에 텍스쳐를 입혀 보겠습니다.</p> + +<h2 id="텍스쳐_로딩">텍스쳐 로딩</h2> + +<p>가장 먼저 해야할 일은 텍스쳐를 읽어오는 것입니다. 이번 예제에서는 동일한 하나의 텍스쳐를 회전하는 정육면체의 6개의 면에 입혀볼 것입니다. 여러개의 텍스쳐를 각 면에 입힌다고 해도 하나를 입히는 것과 동일한 방법을 적용하면 됩니다.</p> + +<div class="note"><strong>Note:</strong> 텍스쳐를 외부에서 읽어올 때는 <a href="/En/HTTP_access_control">크로스 도메인 규칙(cross-domain rules)</a>에 유의해야 합니다. CORS(Cross Origin Resource Sharing)승인을 받을 수 있는 도메인에 있는 텍스쳐만 읽어올 수 있습니다. 자세한 내용은 <a href="/ko/docs/Web/WebGL/Cross-Domain_Textures">크로스 도메인 텍스쳐(Cross-domain textures)</a>를 참고하세요.</div> + +<p>텍스쳐를 읽어오는 코드는 다음과 같습니다:</p> + +<pre class="brush: js">function initTextures() { + cubeTexture = gl.createTexture(); + cubeImage = new Image(); + cubeImage.onload = function() { handleTextureLoaded(cubeImage, cubeTexture); } + cubeImage.src = "cubetexture.png"; +} + +function handleTextureLoaded(image, texture) { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); + gl.generateMipmap(gl.TEXTURE_2D); + gl.bindTexture(gl.TEXTURE_2D, null); +} +</pre> + +<p><code>initTextures()</code> 루틴은 GL의 <code>createTexture()</code> 함수를 호출해서 GL의 텍스쳐 객체인 <code>cubeTexture</code>를 생성하는 걸로 시작됩니다. 그리고 <code>Image</code> 객체를 생성해서 텍스쳐로 사용하기 위해 로딩한 이미지 파일을 <code>Image</code> 객체에 저장합니다. <code>handleTextureLoaded()</code> 콜백 루틴은 이미지 로딩이 완료되면 실행됩니다.</p> + +<p>텍스쳐를 실질적으로 생성하려면, 앞에서 새로 생성한 텍스쳐 객체를 <code>gl.TEXTURE_2D</code>에 바인딩해야 합니다. 그리고 나서 이미지 데이터가 로딩된 이미지 객체를 <code>texImage2D()</code>에 전달하여 호출하면, 이미지 데이터가 텍스쳐에 쓰여(write) 집니다.</p> + +<div class="note"><strong>Note:</strong> 텍스쳐의 너비와 높이는 <strong>거의 대부분</strong>의 상황에서 2의 거듭제곱 픽셀(1, 2, 4, 8, 16, 32, ...)이어야 합니다. 예외인 경우에 대해서는 아래의 <em>"</em><a href="/ko/docs/Web/WebGL/Using_textures_in_WebGL#Non_power-of-two_textures" style="font-style: italic; line-height: 16.7999992370605px;" title="/en-US/docs/Web/WebGL/Using_textures_in_WebGL#Using_non_Power-Of-Two_textures">크기가 2의 거듭제곱 픽셀이 아닌 텍스쳐</a><em>"를 참고하세요.</em></div> + +<p>그 다음 두 라인은 텍스쳐를 위한 필터링을 준비합니다. 이 필터링은 이미지 크기가 변경될 때 이미지가 필터되는 방식을 제어합니다. 여기에서는 이미지를 확대할 때 선형 필터링을 사용하고, 이미지를 축소할 때 mipmap을 사용합니다. <span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">generateMipMap()</span>을 호출해서 mipmap이 만들어지면 <span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">gl.TEXTURE_2D</span>에 null을 바인딩시켜서, 텍스쳐를 다룰 준비가 끝났다는 것을 <span style="line-height: 16.7999992370605px;">WebGL에게 </span>알려줍니다.</p> + +<h3 id="크기가_2의_거듭제곱이_아닌_텍스쳐">크기가 2의 거듭제곱이 아닌 텍스쳐</h3> + +<p>일반적으로 너비와 높이가 2의 거듭제곱인 텍스쳐를 사용하는 것이 가장 이상적입니다. 왜냐하면 2의 거듭제곱인 텍스쳐는 비디오 메모리에 효율적으로 저장될 수 있고, 어떤 방식으로 사용되어야만 한다는 제약이 없기 때문입니다. 예술가들이 이미 작성한 텍스쳐는 너비와 높이가 2의 거듭제곱이 되도록 크기를 맞춰줘야 하며, 가능하다면 아예 만들때부터 2의 거듭제곱으로 만드는 것이 좋습니다. 너비와 높이는 2의 거듭제곱인 <span style="line-height: 16.7999992370605px;">1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 또는 2048 픽셀이어야 합니다. 전부는 아니지만 많은 디바이스가 4096 픽셀도 지원하고 있으며, 어떤 디바이스는 8192 픽셀 이상을 지원하기도 합니다.</span></p> + +<p>2의 거듭제곱인 텍스쳐를 사용하기 곤란한 상황도 있을 수 있습니다. 텍스쳐의 소스가 되는 이미지를 써드파티에서 구한 것이라면, WebGL에 전달하기 전에 HTML5 캔버스를 이용해서 이미지 크기를 2의 거듭제곱으로 수정하는 것이 좋습니다. 이 때 UV 좌표값도 함께 조정해야 합니다.</p> + +<p>2의 거듭제곱이 아닌(NPOT, Non Power Of Two) 텍스쳐를 <strong>꼭 써야만 하는</strong> 상황도 있을 것입니다. WebGL은 NPOT 텍스쳐도 제한적으로 지원합니다. 텍스쳐의 크기가 모니터 해상도와 똑같아야만 한다거나, 위의 단락에서 언급한 것처럼 2의 거듭제곱으로 수정하는 일이 단순히 귀찮을 때는 NPOT 텍스쳐가 유용할 수 있습니다. 하지만 NPOT 텍스쳐에는 제약 사항이 있습니다. NPOT 텍스쳐는 <strong>mipmapping을 할 수 없으며</strong>, 타일(tile) 또는 감싸기(wrap) 처럼 <strong>"반복"하는 방식으로 사용할 수 없습니다</strong>.</p> + +<p>몇 개의 벽돌 이미지를 타일링 해서 벽돌로 된 벽을 만드는 것이 텍스쳐 반복의 한 사례 입니다.</p> + +<p><code>bindTexture()</code>를 이용해서 텍스쳐를 생성할 때, <code>texParameteri()</code> 메서드로 <span style="line-height: 16.7999992370605px;">mipmapping과 UV 반복을 </span>비활성화 시킬 수 있습니다. 이 비활성화를 통해 mipmapping, UV 감싸기, UV 타일링을 포기하고, 디바이스가 텍스쳐를 어떻게 처리할지 결정할 수 있는 제어권도 포기하는 대신 NPOT 텍스쳐를 사용할 수 있게 됩니다.</p> + +<pre>gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //gl.LINEAR 대신에 gl.NEAREST도 허용되지만, 둘 다 mipmap 될 수 없다. +gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); //s좌표계 감싸기(반복) 방지 +<span style="line-height: 1.572;">gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //t좌표계 감싸기(반복) 방지</span></pre> + +<p><code style="font-style: normal; line-height: 16.7999992370605px;">texParameteri()</code><span style="line-height: 16.7999992370605px;"> 메서드에 </span>위와 같은 파라미터를 전달함으로써, WebGL을 지원하는 디바이스는 어떤 해상도의 텍스쳐든 처리할 수 있는 최대한의 해상도까지 자동으로 처리할 수 있게 됩니다. 위와 같은 설정을 해주지 않으면 WebGL은 NPOT 텍스쳐를 처리하지 못하고 <code>rgba(0, 0, 0, 1)</code>인 검은색을 반환합니다.</p> + +<h2 id="면에_텍스쳐_입히기">면에 텍스쳐 입히기</h2> + +<p>이제 텍스쳐 읽어오기는 완료되었고, 텍스쳐도 사용할 준비가 되어 있습니다. 하지만 텍스쳐를 사용하기 전에 텍스쳐의 좌표와 정육면체의 면의 정점을 매핑 시켜줘야 합니다. 이를 위해 <span style="line-height: 16.7999992370605px;"><code>initBuffers()</code> 함수 안에 있던 정육면체 각 면의 색상을 설정하는 내용을 모두 아래와 같은 코드로 대체합니다.</span></p> + +<pre class="brush: js">cubeVerticesTextureCoordBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer); + +var textureCoordinates = [ + // 앞 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + // 뒤 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + // 위 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + // 아래 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + // 오른쪽 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + // 왼쪽 + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 +]; + +gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(textureCoordinates), + gl.STATIC_DRAW); +</pre> + +<p>먼저 각 면의 텍스쳐 좌표를 저장할 GL 버퍼를 생성하고, 텍스쳐 좌표 배열에 바인딩 합니다.</p> + +<p><code style="font-style: normal; line-height: 16.7999992370605px;">textureCoordinates</code><span style="line-height: 16.7999992370605px;"> </span>배열은 정육면체 각 면의 정점에 해당하는 텍스쳐 좌표를 정의합니다. 텍스쳐 좌표값의 범위는 0.0 에서 1.0 사이라는 점을 기억해 주십시오. 텍스쳐 좌표의 너비값과 높이값은 실제 너비값이나 높이값과 관계 없이 언제나 0.0 에서 1.0 사이의 값으로 정규화(normalize) 됩니다.</p> + +<p>텍스쳐 매핑 배열 설정이 끝나고 배열을 버퍼에 전달하면 GL이 텍스쳐 데이터를 사용할 수 있게 됩니다.</p> + +<div class="note">Note: WebKit 기반의 브라우저에서는 <code>WebGLFloatArray</code> 대신에 <code>Float32Array를 사용해야 합니다.</code> </div> + +<h2 id="셰이더_수정">셰이더 수정</h2> + +<p>셰이더 프로그램과 셰이더를 초기화하는 코드들도 단색 색상 대신 텍스쳐를 사용할 수 있도록 수정해야 합니다.</p> + +<p>먼저 <code>initShaders()</code> 안에 있는 아주 단순한 변경 사항을 알아 봅시다:</p> + +<pre class="brush: js">textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); +gl.enableVertexAttribArray(textureCoordAttribute); +</pre> + +<p><span style="line-height: 16.7999992370605px;">정점 컬러 attribute 변수를 설정하던 코드가, </span>각 정점의 텍스쳐 좌표값을 설정하는 코드로 대체 되었습니다. </p> + +<h3 id="정점_셰이더">정점 셰이더</h3> + +<p>다음으로 색상 데이터를 읽어오던 정점 셰이더를 텍스쳐 좌표를 읽어오도록 수정해야 합니다.</p> + +<pre class="brush: html"><script id="shader-vs" type="x-shader/x-vertex"> + attribute vec3 aVertexPosition; + attribute vec2 aTextureCoord; + + uniform mat4 uMVMatrix; + uniform mat4 uPMatrix; + + varying highp vec2 vTextureCoord; + + void main(void) { + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); + vTextureCoord = aTextureCoord; + } +</script> +</pre> + +<p>정점 색상 정보를 읽어오는 대신에 텍스쳐 좌표값을 읽어와서 설정한다는 점이 키 포인트 입니다. 위와 같이 정점과 텍스쳐 좌표값을 매핑하면, 각 정점이 텍스쳐의 어느 지점에 해당 하는지 알려줄 수 있습니다.</p> + +<h3 id="프래그먼트_셰이더">프래그먼트 셰이더</h3> + +<p>마찬가지로 프래그먼트 셰이더도 수정해야 합니다:</p> + +<pre class="brush: html"><script id="shader-fs" type="x-shader/x-fragment"> + varying highp vec2 vTextureCoord; + + uniform sampler2D uSampler; + + void main(void) { + gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); + } +</script> +</pre> + +<p>이렇게 하면 프래그먼트의 색상을 정하기 위해 직접 프래그먼트에 색상값을 할당하지 않고, 샘플러(sampler)가 판단하기에 프래그먼트의 위치에 가장 잘 맞아 떨어진다고 여겨지는 <strong>텍셀(texel, 텍스쳐 내부에 있는 픽셀)</strong>값에 따라서 <span style="line-height: 16.7999992370605px;">프래그먼트의 색상값을 계산해냅니다.</span></p> + +<h2 id="텍스쳐를_입힌_정육면체_그리기">텍스쳐를 입힌 정육면체 그리기</h2> + +<p>텍스쳐를 입힌 상태를 더 명확하게 확인할 수 있도록, 앞 단원의 예제에 포함되어 있던 정육면체의 이동을 제거한 것을 제외하면 drawScene() 함수의 수정은 간단합니다.</p> + +<p>정점에 색상을 매핑하던 코드를 다음과 같이 면에 텍스쳐를 매핑하는 코드로 대체합니다:</p> + +<pre class="brush: js">gl.activeTexture(gl.TEXTURE0); +gl.bindTexture(gl.TEXTURE_2D, cubeTexture); +gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0); +</pre> + +<p>GL은 32개의 텍스쳐 레지스터를 제공합니다. 그 중 첫번째 레지스터는 <code>gl.TEXTURE0</code> 입니다. 텍스쳐를 사용하기 위해 전에 읽어온 텍스쳐를 <span style="font-family: consolas,monaco,andale mono,monospace; line-height: 16.7999992370605px;">gl.TEXTURE0</span>에 바인딩하고, 셰이더 샘플러를 셰이더 프로그램에 명시되어 있는 <code>uSampler</code>로 설정합니다.</p> + +<p>이제 앞 단원의 예제보다 더 보기 좋게 회전하는 정육면체를 볼 수 있을 것입니다. WebGL을 지원하는 브라우저라면 <a href="/samples/webgl/sample6/index.html">여기</a>에서 실제 동작하는 예제를 확인할 수 있습니다.</p> + +<p>{{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}</p> |