-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
178 lines (167 loc) · 7.99 KB
/
index.html
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
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="js/utils.js"></script>
<script src="js/ModelHelper.js"></script>
<script type="module">
import Engine from "./js/engine/engine.js"
import Shapes from "./ressources/shapes/module.js"
import UI from "./ui.js"
document.Engine = Engine
document.Shapes = Shapes
document.UI = UI
</script>
</head>
<body onload="init();start()">
<h1 class="main_title">WebGL Renderer</h1>
<p class="subtitle">Simple 3D model renderer build in WebGL from scratch</p>
<hr>
<br>
<div id="main_content">
<div>
<div id="parameters" class="panel"></div>
<div id="debug_panel" class="panel">
<details>
<summary>Parameters</summary>
<table>
<tr><td>FPS : </td><td id="fps"></td></tr>
<tr><td>Refresh rate : </td><td id="refresh_rate"></td></tr>
</table>
<input onchange="params.depth_test=this.checked;restart()" id="depth_test" type="checkbox" checked/><label>Enable depth testing</label>
<br>
<input onchange="params.show_shadow_map=this.checked;restart()" id="show_shadow_map" type="checkbox" unchecked/><label>Show shadow map</label>
<br>
<label>Depth test function</label>
<select onchange="restart()" id="depth_func">
<option value="NEVER">Never</option>
<option value="ALWAYS">Always</option>
<option value="LESS">Less</option>
<option value="EQUAL">Equal</option>
<option value="LEQUAL" selected="selected">Less or equal</option>
<option value="GREATER">Greater</option>
<option value="GEQUAL">Greater or equal</option>
<option value="NOTEQUAL">Not equal</option>
</select>
<br>
<label>Skybox</label><select id="skybox_select" onchange="skybox=this.value;restart()">
<option value="null">None</option>
<option value="debug">Debug</option>
<option value="field_sunset" selected="selected">Sunset field</option>
<option value="underground">Underground</option>
</select>
</details>
</div>
</div>
<canvas id="canvas" width="800" height="800"></canvas>
</div>
<hr>
<div class="explanations">
<p>
This 3D model viewer is built from scratch in WebGL2. <br>
It is for now a work in progress, and contains some bugs (especially on mobile), but has the following graphic features :
</p>
<ul>
<li>Dynamically generated shaders</li>
<li>One ambient light</li>
<li>One directional light, casting no shadows</li>
<li>One point light, casting omnidirectional shadows and specular</li>
<li>One cone light, casting directional shadows in orthographic perspective</li>
<li>Multiple skyboxes</li>
<li>Textures, for now only one for the whole scene</li>
<li>Reflective material, able to reflect the other objects in the scene, with an arbitrary number of reflection iterations</li>
<li>A few models, with parameters to change their transform and material (see Objects tab)</li>
</ul>
</div>
</body>
<script>
function getById(id) { return document.getElementById(id) }
function getValue(id){ return getById(id).value }
function getChildren(id){ return Array.from(getById(id).children) }
function init(){
getById("parameters").innerHTML = generateTabs("mainTab", [ ["View",""], ["Lights",""], ["Objects",""], ["Shaders",""]])
getById("mainTab_View").style.display = "inline-block";
getById("mainTab").firstChild.classList.add("selectedTab")
Shapes = document.Shapes
Engine = document.Engine
UI = document.UI
updateInterval = 30
previousTime = 0
frameNumber = 0
engine = null
needRestart = false
params = {}
}
function restart(){
console.info("Restarting...")
needRestart = true
}
function start(){
needRestart = false
params.depth_test_function = WebGL2RenderingContext[getValue("depth_func")]
const wavefronts = [{
name:"Dragon",
file:"dragon/dragon_simple2.obj",
transform: [[2,0,0], [0,0,0], [1,1,1]],
material: {}
},{
name:"Teapot",
file:"teapot/teapot_simple.obj",
transform:[[-9.5, 4.3, 0], [0, 0.6, -90], [3.5,3.5,3.5]],
material:{reflectionFactor: 0.45}
}]
engine = new Engine(getById("canvas"), params)
engine.objects.add(Shapes.Plane, "Ground", {}, [-4,-1,-0.15], [0,0,0], [1.2, 1.2, 1.2])
//engine.addObject(Shapes.Plane, "WallX", [-10,0,10], [0,90,0])
//engine.addObject(Shapes.Plane, "WallY", [0,-10,10], [90,0,0])
//engine.addObject(Shapes.Cube, "Cube_1", engine.defaultMaterial, [0,-2,1], [0,0,0], [2,2,2])
/*engine.addObject(Shapes.Cube, "Cube_2", [2,0,1])
engine.addObject(Shapes.Cube, "Cube_3", [0,-2,2])
engine.addObject(Shapes.Cube, "Cube_4", [-2,0,1])*/
engine.setSkybox("ressources/cubemaps/"+getValue("skybox_select"))
UI.generateViewTab(engine)
UI.generateLightsTab()
ModelHelper.loadWavefronts(wavefronts, engine).then( () => UI.generateObjectsTab(engine.objects.list))
UI.generateShadersTab()
render(0)
}
function render(currentTime) {
frameNumber++
const deltaTime = currentTime - previousTime
previousTime = currentTime
if(frameNumber % updateInterval == 0){
getById("fps").innerHTML = Math.round(1000/deltaTime)
getById("refresh_rate").innerHTML = Math.round(deltaTime)+" ms"
}
engine.render()
needRestart ? start() : requestAnimationFrame(render)
}
function addModel(){
const model_id = parseInt(getById("model_selection").value)
let model = null
switch (model_id) {
case 1 :
model = {name:"Dragon", file:"dragon/dragon_simple2.obj"}
break
case 2 :
model = {name:"Teapot", file:"teapot/teapot_simple.obj"}
break
case 3 :
model = {name:"Suzanne", file:"suzanne/suzanne_simple.obj"}
break
case 4 :
model = {name:"Gizmo", file:"gizmo/gizmo.obj"}
break
case 5 :
model = {name:"Sphere", file:"sphere/sphere.obj"}
break
case 6 :
model = {name:"Cube", file:"cube/cube.obj"}
break
default:
return
}
ModelHelper.loadWavefronts([model], engine).then( () => UI.generateObjectsTab(engine.objects.list))
}
</script>
</html>