Repository navigation
Expand file tree
/
Copy pathskybox.js
More file actions
168 lines (137 loc) · 6.41 KB
/
Copy pathskybox.js
File metadata and controls
168 lines (137 loc) · 6.41 KB
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
class Skybox {
constructor(gl, image_url) {
// Create vertex data for a cube
this.vertices = new Float32Array([
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
]);
this.indices = new Uint16Array([
0, 1, 2, 0, 2, 3, // front
3, 2, 6, 3, 6, 7, // right
7, 6, 5, 7, 5, 4, // back
4, 5, 1, 4, 1, 0, // left
4, 0, 3, 4, 3, 7, // top
1, 5, 6, 1, 6, 2 // bottom
]);
// Create and bind vertex buffer
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW);
// Create and bind index buffer
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
// Create the shader program
this.program = create_compile_and_link_program(gl,
// Vertex shader
`#version 300 es
precision highp float;
in vec3 position;
uniform mat4 projection;
uniform mat4 view;
out vec3 texCoords;
void main() {
texCoords = position;
// Remove translation from view matrix by using only the rotation part
mat4 rotView = mat4(mat3(view));
// Increase scale for better coverage
vec4 pos = projection * rotView * vec4(position * 2000.0, 1.0);
gl_Position = pos.xyww;
}`,
// Fragment shader
`#version 300 es
precision highp float;
in vec3 texCoords;
uniform samplerCube skybox;
out vec4 fragColor;
void main() {
fragColor = texture(skybox, texCoords);
}`
);
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);
const placeholder = new Uint8Array([128, 128, 128, 255]);
for (let i = 0; i < 6; i++) {
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, gl.RGBA, 1, 1, 0, gl.RGBA,
gl.UNSIGNED_BYTE, placeholder
);
}
const image = new Image();
image.onload = () => {
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const faceWidth = Math.floor(image.width / 4); // 1024 for 4096 width
const faceHeight = Math.floor(image.height / 3); // 1024 for 3072 height
canvas.width = faceWidth;
canvas.height = faceHeight;
ctx.imageSmoothingEnabled = false;
const faces = [
// Right face (POSITIVE_X) - third in middle row
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, x: faceWidth * 2, y: faceHeight },
// Left face (NEGATIVE_X) - first in middle row
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, x: 0, y: faceHeight },
// Top face (POSITIVE_Y) - second column, first row
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, x: faceWidth, y: 0 },
// Bottom face (NEGATIVE_Y) - second column, third row
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, x: faceWidth, y: faceHeight * 2 },
// Front face (POSITIVE_Z) - second in middle row
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, x: faceWidth, y: faceHeight },
// Back face (NEGATIVE_Z) - fourth in middle row
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, x: faceWidth * 3, y: faceHeight }
];
faces.forEach(face => {
ctx.clearRect(0, 0, faceWidth, faceHeight);
// Draw the face at exact dimensions without scaling
ctx.drawImage(image,
face.x, face.y, faceWidth, faceHeight,
0, 0, faceWidth, faceHeight
);
const faceData = ctx.getImageData(0, 0, faceWidth, faceHeight);
// Upload texture with no scaling
gl.texImage2D(
face.target,
0, gl.RGBA, faceWidth, faceHeight,
0, gl.RGBA, gl.UNSIGNED_BYTE,
faceData
);
});
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
const ext = gl.getExtension('EXT_texture_filter_anisotropic');
if (ext) {
const max = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
gl.texParameterf(gl.TEXTURE_CUBE_MAP, ext.TEXTURE_MAX_ANISOTROPY_EXT, max);
}
};
image.src = image_url;
}
render(gl, projectionMatrix, viewMatrix) {
const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
gl.useProgram(this.program);
set_uniform_matrix4(gl, this.program, 'projection', projectionMatrix);
set_uniform_matrix4(gl, this.program, 'view', viewMatrix);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
const positionLoc = gl.getAttribLocation(this.program, 'position');
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);
const skyboxLoc = gl.getUniformLocation(this.program, 'skybox');
gl.uniform1i(skyboxLoc, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
gl.useProgram(currentProgram);
}
}