182 lines
4.4 KiB
JavaScript
182 lines
4.4 KiB
JavaScript
import Resource from './resource'
|
|
|
|
class Shader {
|
|
constructor (type, source) {
|
|
this.type = type
|
|
this.source = source
|
|
this.id = 0
|
|
}
|
|
|
|
compile (gl) {
|
|
const shader = gl.createShader(this.type)
|
|
|
|
// Send the source to the shader object
|
|
gl.shaderSource(shader, this.source)
|
|
|
|
// Compile the shader program
|
|
gl.compileShader(shader)
|
|
|
|
// See if it compiled successfully
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
let inf = gl.getShaderInfoLog(shader)
|
|
gl.deleteShader(shader)
|
|
throw new Error('An error occurred compiling the shaders: ' + inf)
|
|
}
|
|
|
|
this.id = shader
|
|
|
|
return this
|
|
}
|
|
}
|
|
|
|
class ShaderProgram {
|
|
constructor (name) {
|
|
this.name = name
|
|
this.id = 0
|
|
|
|
this.vertexShader = null
|
|
this.geometryShader = null
|
|
this.fragmentShader = null
|
|
|
|
this.uniforms = {}
|
|
this.attribs = {}
|
|
}
|
|
|
|
link (gl, vs, fs, gs) {
|
|
let vsh = [ vs, fs, gs ]
|
|
|
|
for (let i in vsh) {
|
|
vsh[i] && vsh[i].compile(gl)
|
|
}
|
|
|
|
const shaderProgram = gl.createProgram()
|
|
|
|
// Attach the shaders
|
|
gl.attachShader(shaderProgram, vs.id)
|
|
gs && gl.attachShader(shaderProgram, gs.id)
|
|
gl.attachShader(shaderProgram, fs.id)
|
|
|
|
// Link the program
|
|
gl.linkProgram(shaderProgram)
|
|
|
|
// If creating the shader program failed, error
|
|
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
|
throw new Error('Unable to initialize the shader program ' + this.name + ': ' + gl.getProgramInfoLog(shaderProgram))
|
|
}
|
|
|
|
this.id = shaderProgram
|
|
this.vertexShader = vs
|
|
this.fragmentShader = fs
|
|
this.geometryShader = gs
|
|
|
|
// Detach shaders after use
|
|
gl.detachShader(shaderProgram, vs.id)
|
|
gs && gl.detachShader(shaderProgram, gs.id)
|
|
gl.detachShader(shaderProgram, fs.id)
|
|
|
|
return this
|
|
}
|
|
|
|
getUniformLocation (gl, name) {
|
|
if (this.uniforms[name]) return this.uniforms[name]
|
|
let uni = gl.getUniformLocation(this.id, name)
|
|
if (uni < 0) return null
|
|
|
|
this.uniforms[name] = uni
|
|
return uni
|
|
}
|
|
|
|
getAttributeLocation (gl, name) {
|
|
if (this.attribs[name]) return this.attribs[name]
|
|
let pos = gl.getAttribLocation(this.id, name)
|
|
if (pos < 0) throw new Error(`No such attribute location ${name} in shader ${this.name}!`)
|
|
this.attribs[name] = pos
|
|
return pos
|
|
}
|
|
|
|
hasAttribute (gl, name) {
|
|
try {
|
|
this.getAttributeLocation(gl, name)
|
|
return true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
setAttribute (gl, name, size, normalized, stride, offset, type) {
|
|
let loc = this.getAttributeLocation(gl, name) // throws an error in case the name doesn't exist in shader
|
|
|
|
gl.enableVertexAttribArray(loc)
|
|
gl.vertexAttribPointer(
|
|
loc,
|
|
size,
|
|
type || gl.FLOAT,
|
|
normalized,
|
|
stride,
|
|
offset)
|
|
}
|
|
|
|
use (gl) {
|
|
if (this.id === 0) return
|
|
gl.useProgram(this.id)
|
|
}
|
|
}
|
|
|
|
class ShaderManager {
|
|
constructor () {
|
|
this.shaders = {}
|
|
}
|
|
|
|
createShader (gl, name, vs, fs, gs) {
|
|
if (this.shaders[name]) return this.shaders[name]
|
|
let shader = new ShaderProgram(name)
|
|
|
|
let vert = new Shader(gl.VERTEX_SHADER, vs)
|
|
let frag = new Shader(gl.FRAGMENT_SHADER, fs)
|
|
let geom = gs ? new Shader(gl.GEOMETRY_SHADER, gs) : null
|
|
|
|
shader.link(gl, vert, frag, geom)
|
|
|
|
this.shaders[name] = shader
|
|
return shader
|
|
}
|
|
|
|
// Standard shader nomenclature: /assets/shaders/shader-name.vs|fs[|gs]
|
|
// shader-name.vs and shader-name.fs are mandatory!
|
|
createShaderFromFiles (gl, name, gs) {
|
|
let stdloc = '/assets/shaders/' + name
|
|
|
|
return new Promise((resolve, reject) => {
|
|
function finishLink (vs, fs, gss) {
|
|
try {
|
|
let shader = this.createShader(gl, name, vs, fs, gss)
|
|
resolve(shader)
|
|
} catch (e) {
|
|
reject(e)
|
|
}
|
|
}
|
|
|
|
Resource.GET(stdloc + '.vs').then((vs) => {
|
|
Resource.GET(stdloc + '.fs').then((fs) => {
|
|
if (gs !== false) {
|
|
// Try to find the geometry shader if it wasn't explicitly stated to not exist
|
|
return Resource.GET(stdloc + '.gs').then((gss) => {
|
|
finishLink.apply(this, [vs, fs, gss])
|
|
}, () => {
|
|
finishLink.apply(this, [vs, fs])
|
|
})
|
|
}
|
|
|
|
finishLink.apply(this, [vs, fs, null])
|
|
}, reject)
|
|
}, reject)
|
|
})
|
|
}
|
|
|
|
use (gl, name) {
|
|
if (this.shaders[name]) this.shaders[name].use(gl)
|
|
}
|
|
}
|
|
|
|
export { Shader, ShaderProgram, ShaderManager }
|