/
glshader.js
executable file
·158 lines (140 loc) · 5.07 KB
/
glshader.js
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
import * as event from "./../../system/event.js";
import { extractUniforms } from "./utils/uniforms.js";
import { extractAttributes } from "./utils/attributes.js";
import { compileProgram } from "./utils/program.js";
import { setPrecision, getMaxShaderPrecision } from "./utils/precision.js";
import { minify } from "./utils/string.js";
/**
* @classdesc
* a base GL Shader object
*/
export default class GLShader {
/**
* @param {WebGLRenderingContext} gl - the current WebGL rendering context
* @param {string} vertex - a string containing the GLSL source code to set
* @param {string} fragment - a string containing the GLSL source code to set
* @param {string} [precision=auto detected] - float precision ('lowp', 'mediump' or 'highp').
* @see https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders
* @example
* // create a basic shader
* let myShader = new me.GLShader(
* // WebGL rendering context
* gl,
* // vertex shader
* [
* "void main() {",
* " gl_Position = doMathToMakeClipspaceCoordinates;",
* "}"
* ].join("\n"),
* // fragment shader
* [
* "void main() {",
* " gl_FragColor = doMathToMakeAColor;",
* "}"
* ].join("\n")
* )
* // use the shader
* myShader.bind();
*/
constructor(gl, vertex, fragment, precision) {
/**
* the active gl rendering context
* @type {WebGLRenderingContext}
*/
this.gl = gl;
/**
* the vertex shader source code
* @type {string}
*/
this.vertex = setPrecision(minify(vertex), precision || getMaxShaderPrecision(this.gl));
/**
* the fragment shader source code
* @type {string}
*/
this.fragment = setPrecision(minify(fragment), precision || getMaxShaderPrecision(this.gl));
/**
* the location attributes of the shader
* @type {GLint[]}
*/
this.attributes = extractAttributes(this.gl, this);
/**
* a reference to the shader program (once compiled)
* @type {WebGLProgram}
*/
this.program = compileProgram(this.gl, this.vertex, this.fragment, this.attributes);
/**
* the uniforms of the shader
* @type {object}
*/
this.uniforms = extractUniforms(this.gl, this);
// destroy the shader on context lost (will be recreated on context restore)
event.on(event.ONCONTEXT_LOST, this.destroy, this);
}
/**
* Installs this shader program as part of current rendering state
*/
bind() {
this.gl.useProgram(this.program);
}
/**
* returns the location of an attribute variable in this shader program
* @param {string} name - the name of the attribute variable whose location to get.
* @returns {GLint} number indicating the location of the variable name if found. Returns -1 otherwise
*/
getAttribLocation(name) {
let attr = this.attributes[name];
if (typeof attr !== "undefined") {
return attr;
} else {
return -1;
}
}
/**
* Set the uniform to the given value
* @param {string} name - the uniform name
* @param {object|Float32Array} value - the value to assign to that uniform
* @example
* myShader.setUniform("uProjectionMatrix", this.projectionMatrix);
*/
setUniform(name, value) {
let uniforms = this.uniforms;
if (typeof uniforms[name] !== "undefined") {
if (typeof value === "object" && typeof value.toArray === "function") {
uniforms[name] = value.toArray();
} else {
uniforms[name] = value;
}
} else {
throw new Error("undefined (" + name + ") uniform for shader " + this);
}
}
/**
* activate the given vertex attribute for this shader
* @param {WebGLRenderingContext} gl - the current WebGL rendering context
* @param {object[]} attributes - an array of vertex attributes
* @param {number} vertexByteSize - the size of a single vertex in bytes
*/
setVertexAttributes(gl, attributes, vertexByteSize) {
// set the vertex attributes
for (let index = 0; index < attributes.length; ++index) {
let element = attributes[index];
let location = this.getAttribLocation(element.name);
if (location !== -1) {
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, element.size, element.type, element.normalized, vertexByteSize, element.offset);
} else {
gl.disableVertexAttribArray(index);
}
}
}
/**
* destroy this shader objects resources (program, attributes, uniforms)
*/
destroy() {
this.uniforms = null;
this.attributes = null;
this.gl.deleteProgram(this.program);
this.vertex = null;
this.fragment = null;
}
}