WebGL
을 이용하여 Omnidirectional Panorama
를 구현하여 보았다. Mouse Drag
를 통해 View Direction
을 변경할 수 있다. WebGL을 지원하는 브라우저에서만 정상 동작 할 것이다. OpenGL(WebGL)의 몇 가지 특성상 텍스쳐의 가로 및 세로의 크기가 2의 제곱수(power of two)가 되어야 정상 동작하는 일부분이 있다(GL_TEXTURE_WRAP_S
옵션에서 GL_REPEAT
로 설정 등).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
var HALF_PI = Math.PI / 2;
var DOUBLE_PI = Math.PI * 2;
Matrix = function () {
this.elements = new Float32Array(16);
}
Matrix.prototype.flattern = function () {
return this.elements;
}
Matrix.prototype.toString = function () {
return this.elements[0] + ", " + this.elements[1] + ", " + this.elements[2] + ", " + this.elements[3] + "\n" +
this.elements[4] + ", " + this.elements[5] + ", " + this.elements[6] + ", " + this.elements[7] + "\n" +
this.elements[8] + ", " + this.elements[9] + ", " + this.elements[10] + ", " + this.elements[11] + "\n" +
this.elements[12] + ", " + this.elements[13] + ", " + this.elements[14] + ", " + this.elements[15];
}
Matrix.prototype.makeIdentity = function () {
this.elements[1] = this.elements[2] = this.elements[3] = this.elements[4] =
this.elements[6] = this.elements[7] = this.elements[8] = this.elements[9] =
this.elements[11] = this.elements[12] = this.elements[13] = this.elements[14] = 0;
this.elements[0] = this.elements[5] = this.elements[10] = this.elements[15] = 1;
}
Matrix.prototype.translate = function (x, y, z) {
this.elements[1] = this.elements[2] = this.elements[3] = this.elements[4] =
this.elements[6] = this.elements[7] = this.elements[8] = this.elements[9] = this.elements[11] = 0;
this.elements[0] = this.elements[5] = this.elements[10] = this.elements[15] = 1;
this.elements[12] = x;
this.elements[13] = y;
this.elements[14] = z;
}
Matrix.prototype.rotationX = function (angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
this.elements[0] = 1;
this.elements[1] = 0;
this.elements[2] = 0;
this.elements[3] = 0;
this.elements[4] = 0;
this.elements[5] = c;
this.elements[6] = s;
this.elements[7] = 0;
this.elements[8] = 0;
this.elements[9] = -s;
this.elements[10] = c;
this.elements[11] = 0;
this.elements[12] = 0;
this.elements[13] = 0;
this.elements[14] = 0;
this.elements[15] = 1;
}
Matrix.prototype.rotationY = function (angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
this.elements[0] = c;
this.elements[1] = 0;
this.elements[2] = s;
this.elements[3] = 0;
this.elements[4] = 0;
this.elements[5] = 1;
this.elements[6] = 0;
this.elements[7] = 0;
this.elements[8] = -s;
this.elements[9] = 0;
this.elements[10] = c;
this.elements[11] = 0;
this.elements[12] = 0;
this.elements[13] = 0;
this.elements[14] = 0;
this.elements[15] = 1;
}
Matrix.prototype.rotationZ = function (angle) {
var c = Math.cos(angle);
var s = Math.sin(angle);
this.elements[0] = c;
this.elements[1] = s;
this.elements[2] = 0;
this.elements[3] = 0;
this.elements[4] = -s;
this.elements[5] = c;
this.elements[6] = 0;
this.elements[7] = 0;
this.elements[8] = 0;
this.elements[9] = 0;
this.elements[10] = 0;
this.elements[11] = 0;
this.elements[12] = 0;
this.elements[13] = 0;
this.elements[14] = 0;
this.elements[15] = 1;
}
Matrix.prototype.perspective = function (fov, aspect, zNear, zFar) {
var h = 1.0 / Math.tan(fov * Math.PI / 360);
var neg_depth = zNear - zFar;
this.elements[0] = h / aspect;
this.elements[1] = 0;
this.elements[2] = 0;
this.elements[3] = 0;
this.elements[4] = 0;
this.elements[5] = h;
this.elements[6] = 0;
this.elements[7] = 0;
this.elements[8] = 0;
this.elements[9] = 0;
this.elements[10] = (zFar + zNear) / neg_depth;
this.elements[11] = -1;
this.elements[12] = 0;
this.elements[13] = 0;
this.elements[14] = 2.0 * (zNear * zFar) / neg_depth;
this.elements[15] = 0;
}
Matrix.prototype.mul = function (rhs) {
var result = new Matrix();
result.elements[0] = this.elements[0] * rhs.elements[0] + this.elements[1] * rhs.elements[4] + this.elements[2] * rhs.elements[8] + this.elements[3] * rhs.elements[12];
result.elements[1] = this.elements[0] * rhs.elements[1] + this.elements[1] * rhs.elements[5] + this.elements[2] * rhs.elements[9] + this.elements[3] * rhs.elements[13];
result.elements[2] = this.elements[0] * rhs.elements[2] + this.elements[1] * rhs.elements[6] + this.elements[2] * rhs.elements[10] + this.elements[3] * rhs.elements[14];
result.elements[3] = this.elements[0] * rhs.elements[3] + this.elements[1] * rhs.elements[7] + this.elements[2] * rhs.elements[11] + this.elements[3] * rhs.elements[15];
result.elements[4] = this.elements[4] * rhs.elements[0] + this.elements[5] * rhs.elements[4] + this.elements[6] * rhs.elements[8] + this.elements[7] * rhs.elements[12];
result.elements[5] = this.elements[4] * rhs.elements[1] + this.elements[5] * rhs.elements[5] + this.elements[6] * rhs.elements[9] + this.elements[7] * rhs.elements[13];
result.elements[6] = this.elements[4] * rhs.elements[2] + this.elements[5] * rhs.elements[6] + this.elements[6] * rhs.elements[10] + this.elements[7] * rhs.elements[14];
result.elements[7] = this.elements[4] * rhs.elements[3] + this.elements[5] * rhs.elements[7] + this.elements[6] * rhs.elements[11] + this.elements[7] * rhs.elements[15];
result.elements[8] = this.elements[8] * rhs.elements[0] + this.elements[9] * rhs.elements[4] + this.elements[10] * rhs.elements[8] + this.elements[11] * rhs.elements[12];
result.elements[9] = this.elements[8] * rhs.elements[1] + this.elements[9] * rhs.elements[5] + this.elements[10] * rhs.elements[9] + this.elements[11] * rhs.elements[13];
result.elements[10] = this.elements[8] * rhs.elements[2] + this.elements[9] * rhs.elements[6] + this.elements[10] * rhs.elements[10] + this.elements[11] * rhs.elements[14];
result.elements[11] = this.elements[8] * rhs.elements[3] + this.elements[9] * rhs.elements[7] + this.elements[10] * rhs.elements[11] + this.elements[11] * rhs.elements[15];
result.elements[12] = this.elements[12] * rhs.elements[0] + this.elements[13] * rhs.elements[4] + this.elements[14] * rhs.elements[8] + this.elements[15] * rhs.elements[12];
result.elements[13] = this.elements[12] * rhs.elements[1] + this.elements[13] * rhs.elements[5] + this.elements[14] * rhs.elements[9] + this.elements[15] * rhs.elements[13];
result.elements[14] = this.elements[12] * rhs.elements[2] + this.elements[13] * rhs.elements[6] + this.elements[14] * rhs.elements[10] + this.elements[15] * rhs.elements[14];
result.elements[15] = this.elements[12] * rhs.elements[3] + this.elements[13] * rhs.elements[7] + this.elements[14] * rhs.elements[11] + this.elements[15] * rhs.elements[15];
this.elements = result.elements;
}
function MatrixMultiply(lhs, rhs) {
var result = new Matrix();
result.elements[0] = lhs.elements[0] * rhs.elements[0] + lhs.elements[1] * rhs.elements[4] + lhs.elements[2] * rhs.elements[8] + lhs.elements[3] * rhs.elements[12];
result.elements[1] = lhs.elements[0] * rhs.elements[1] + lhs.elements[1] * rhs.elements[5] + lhs.elements[2] * rhs.elements[9] + lhs.elements[3] * rhs.elements[13];
result.elements[2] = lhs.elements[0] * rhs.elements[2] + lhs.elements[1] * rhs.elements[6] + lhs.elements[2] * rhs.elements[10] + lhs.elements[3] * rhs.elements[14];
result.elements[3] = lhs.elements[0] * rhs.elements[3] + lhs.elements[1] * rhs.elements[7] + lhs.elements[2] * rhs.elements[11] + lhs.elements[3] * rhs.elements[15];
result.elements[4] = lhs.elements[4] * rhs.elements[0] + lhs.elements[5] * rhs.elements[4] + lhs.elements[6] * rhs.elements[8] + lhs.elements[7] * rhs.elements[12];
result.elements[5] = lhs.elements[4] * rhs.elements[1] + lhs.elements[5] * rhs.elements[5] + lhs.elements[6] * rhs.elements[9] + lhs.elements[7] * rhs.elements[13];
result.elements[6] = lhs.elements[4] * rhs.elements[2] + lhs.elements[5] * rhs.elements[6] + lhs.elements[6] * rhs.elements[10] + lhs.elements[7] * rhs.elements[14];
result.elements[7] = lhs.elements[4] * rhs.elements[3] + lhs.elements[5] * rhs.elements[7] + lhs.elements[6] * rhs.elements[11] + lhs.elements[7] * rhs.elements[15];
result.elements[8] = lhs.elements[8] * rhs.elements[0] + lhs.elements[9] * rhs.elements[4] + lhs.elements[10] * rhs.elements[8] + lhs.elements[11] * rhs.elements[12];
result.elements[9] = lhs.elements[8] * rhs.elements[1] + lhs.elements[9] * rhs.elements[5] + lhs.elements[10] * rhs.elements[9] + lhs.elements[11] * rhs.elements[13];
result.elements[10] = lhs.elements[8] * rhs.elements[2] + lhs.elements[9] * rhs.elements[6] + lhs.elements[10] * rhs.elements[10] + lhs.elements[11] * rhs.elements[14];
result.elements[11] = lhs.elements[8] * rhs.elements[3] + lhs.elements[9] * rhs.elements[7] + lhs.elements[10] * rhs.elements[11] + lhs.elements[11] * rhs.elements[15];
result.elements[12] = lhs.elements[12] * rhs.elements[0] + lhs.elements[13] * rhs.elements[4] + lhs.elements[14] * rhs.elements[8] + lhs.elements[15] * rhs.elements[12];
result.elements[13] = lhs.elements[12] * rhs.elements[1] + lhs.elements[13] * rhs.elements[5] + lhs.elements[14] * rhs.elements[9] + lhs.elements[15] * rhs.elements[13];
result.elements[14] = lhs.elements[12] * rhs.elements[2] + lhs.elements[13] * rhs.elements[6] + lhs.elements[14] * rhs.elements[10] + lhs.elements[15] * rhs.elements[14];
result.elements[15] = lhs.elements[12] * rhs.elements[3] + lhs.elements[13] * rhs.elements[7] + lhs.elements[14] * rhs.elements[11] + lhs.elements[15] * rhs.elements[15];
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Texture= function(src){
this.texture= null;
var img= new Image();
img.onload= function(){
/*
* Image Loading이 완료되었을 때 수행되는 inner 함수.
* Loading의 시작은 Image클래스의 src변수에 이미지가 위치한 주소를 할당 할 때 이다.
*/
this.texture= gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
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.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
//gl.generateMipmap(gl.TEXTURE_2D);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
}
img.src= src;
}
Texture.prototype.getTexture= function(){
return this.texture;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
<html>
<head>
<title>WebGL - Prac11 - Sphere</title>
<script src="glMath.js" type="text/javascript"></script>
<script src="glUtil.js" type="text/javascript"></script>
<script type="text/javascript">
var strContext = "experimental-webgl";
var canvas = null;
var gl = null;
var vertBuf = null;
var idxBuf = null;
var texture = null;
var g_hVertPos = null;
var g_hProj = null;
var g_hWorld = null;
var g_hSampler1 = null;
var equirectangularTextureURL = "./hallym.jpg";
var bRotateBtnDown = false;
var prevMousePosX = 0;
var prevMousePosY = 0;
var rotationX = 0;
var rotationY = 0;
var matrixProj = new Matrix();
var matrixRotX = new Matrix();
var matrixRotY = new Matrix();
var fovy = 60.0;
function start() {
canvas = document.getElementById("webglCanvas");
canvas.onmousedown = onMouseEvent;
canvas.onmouseup = onMouseEvent;
canvas.onmousemove = onMouseEvent;
canvas.onmousewheel = onMouseEvent;
gl = canvas.getContext(strContext);
if (gl == null) {
alert("getContext(\"" + strContext + "\") error!");
}
initShader();
var vertData = [];
var idxData = [];
createSphereMesh(vertData, null, idxData);
vertBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertData), gl.STATIC_DRAW);
vertBuf.itemSize = 3;
vertBuf.numItems = vertData.length / vertBuf.itemSize;
idxBuf = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idxData), gl.STATIC_DRAW);
idxBuf.itemSize = 3;
idxBuf.numItems = idxData.length;
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
gl.vertexAttribPointer(g_hVertPos, 3, gl.FLOAT, false, 0, 0);
texture = new Texture(equirectangularTextureURL);
gl.enable(gl.TEXTURE_2D);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture.getTexture());
gl.uniform1i(g_hSampler1, 0);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
setInterval(drawScene, 30);
}
function onMouseEvent(e) {
switch (e.type) {
case "mousedown":
if (e.button == 0) {
bRotateBtnDown = true;
prevMousePosX = e.offsetX;
prevMousePosY = e.offsetY;
}
break;
case "mouseup":
bRotateBtnDown = false;
break;
case "mousemove":
if (bRotateBtnDown) {
rotationY += (prevMousePosX - e.offsetX) * (fovy / 10000);
rotationX -= (prevMousePosY - e.offsetY) * (fovy / 10000);
if (rotationX > HALF_PI) rotationX = HALF_PI;
else if (rotationX < -HALF_PI) rotationX = -HALF_PI;
prevMousePosX = e.offsetX;
prevMousePosY = e.offsetY;
}
break;
case "mousewheel":
fovy += (e.wheelDelta > 0 ? -1.0 : 1.0) * (fovy / 6);
if (fovy < 15) fovy = 15;
else if (fovy > 140) fovy = 140;
break;
}
e.preventDefault();
e.stopPropagation();
return false;
}
function drawScene() {
//World Matrix
matrixRotX.rotationX(rotationX);
matrixRotY.rotationY(rotationY);
gl.uniformMatrix4fv(g_hWorld, false, MatrixMultiply(matrixRotY, matrixRotX).flattern());
//Proj Matrix
matrixProj.perspective(fovy, canvas.width / canvas.height, 0.1, 2000);
gl.uniformMatrix4fv(g_hProj, false, matrixProj.flattern());
gl.clearColor(0.4, 0.5, 0.6, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuf);
//gl.drawElements(gl.LINE_LOOP, idxBuf.numItems, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.TRIANGLES, idxBuf.numItems, gl.UNSIGNED_SHORT, 0);
}
function createSphereMesh(vertData, texData, idxData) {
//박스의 크기
var s = 500;
vertData.push(-s, s, -s);
vertData.push(s, s, -s);
vertData.push(-s, -s, -s);
vertData.push(s, -s, -s);
vertData.push(-s, s, s);
vertData.push(s, s, s);
vertData.push(-s, -s, s);
vertData.push(s, -s, s);
idxData.push(0, 2, 1);//Front
idxData.push(1, 2, 3);
idxData.push(4, 5, 6);//Back
idxData.push(5, 7, 6);
idxData.push(0, 4, 2);//Left
idxData.push(2, 4, 6);
idxData.push(1, 3, 5);//Right
idxData.push(5, 3, 7);
idxData.push(0, 5, 4);//Top
idxData.push(0, 1, 5);
idxData.push(2, 6, 7);//Bottom
idxData.push(3, 2, 7);
/* Vertex Shader에서 Texture Coordinate가 계산되므로 굳이 버텍스가 많은 Sphere를 생성할 필요는 없이, Box만으로도 충분 하다.
var nSlice= 3;
var nStack= 3;
var radius= 500;
//Create Sphere Vertices
for(var i=0; i<=nStack; ++i){
var theta= i * Math.PI / nSlice;
var sinTheta= Math.sin(theta);
var cosTheta= Math.cos(theta);
for(var j=0; j<=nSlice; ++j){
var phi= j * 2 * Math.PI / nStack;
var sinPhi= Math.sin(phi);
var cosPhi= Math.cos(phi);
var x= cosPhi * sinTheta;
var y= cosTheta;
var z= sinPhi * sinTheta;
vertData.push(radius*x, radius*y, radius*z);
//Spherical Panorama에서의 Texture Coordinate는 Fragment Shader에서 처리하므로, 필요하지 않다.
//Fragment Shader에서 Texture Coordinate를 계산하는 것이 보다 느릴 수 있으나,
//Vertex 당 Texture Coordinate를 할당하는 것 보다 정확도 면에서 훨씬 이득이라고 판단된다.
//var u= 1.0 - (j/nSlice);
//var v= 1.0-(i / nStack);
//texData.push(u, v);
}
}
//Create Sphere Indices
for(var i=0; i<nStack; ++i){
for(var j=0; j<nSlice; ++j){
var first= (i*(nStack+1))+j;
var second= first+nStack+1;
idxData.push(first, second, first+1);
idxData.push(second, second+1, first+1);
}
}
*/
}
function initShader() {
var vertexShaderDesc = getShader("VertexShader");
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderDesc);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert("Error(VertexShader): " + gl.getShaderInfoLog(vertexShader));
return false;
}
var fragmentShaderDesc = getShader("FragmentShader");
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderDesc);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert("Error(FragmentShader): " + gl.getShaderInfoLog(fragmentShader));
return false;
}
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Unagle to initialize the shader program.");
return false;
}
gl.useProgram(shaderProgram);
g_hVertPos = gl.getAttribLocation(shaderProgram, "aPos");
if (g_hVertPos == -1) {
alert("Unable to find Vertex Attribute Location.");
return false;
}
gl.enableVertexAttribArray(g_hVertPos);
g_hProj = gl.getUniformLocation(shaderProgram, "gProj");
if (g_hWorld == -1) {
alert("Unable to find 'gView' uniform location.");
return false;
}
g_hWorld = gl.getUniformLocation(shaderProgram, "gWorld");
if (g_hWorld == -1) {
alert("Unable to find 'gView' uniform location.");
return false;
}
g_hSampler1 = gl.getUniformLocation(shaderProgram, "gSampler1");
if (g_hSampler1 == -1) {
alert("Unable to find 'gSampler1' uniform location.");
return false;
}
return true;
}
function getShader(id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
alert("Unable to get shader script. id='" + id + "'");
return null;
}
var shaderDesc = "";
var currentChild = shaderScript.firstChild;
while (currentChild) {
if (currentChild.nodeType == 3) {
shaderDesc += currentChild.textContent;
}
currentChild = currentChild.nextSibling;
}
return shaderDesc;
}
</script>
<script id="VertexShader" type="x-shader/x-vertex">
attribute vec3 aPos;
uniform mat4 gProj;
uniform mat4 gWorld;
varying vec3 vTex1;
void main(){
mat4 WVP= gProj*gWorld;
gl_Position= WVP * vec4(aPos, 1);
vTex1= normalize(aPos);
}
</script>
<script id="FragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vTex1;
uniform sampler2D gSampler1;
void main(){
float theta= atan(vTex1.z, vTex1.x);
float phi= acos(vTex1.y/sqrt(vTex1.x*vTex1.x+vTex1.y*vTex1.y+vTex1.z*vTex1.z));
float u= theta / 3.14159265 / 2.0;
float v= phi / 3.14159265;
gl_FragColor= texture2D(gSampler1, vec2(u, v));
}
</script>
<style>
html, body {
padding: 0;
margin: 0;
}
</style>
</head>
<body onload="start()">
<canvas id="webglCanvas" style="border: 1px solid black" width="600" height="450">
Cannot run WebGL
</canvas>
</body>
</html>
사용된 equirectangular projected texture
는 아래와 같다. 파일명은 hallym.jpg
이다.
Adobe Flash Version은 이곳에서 다운로드 가능하다.