121 lines
3.1 KiB
TypeScript
121 lines
3.1 KiB
TypeScript
import passthroughVertexShader from '../../../shaders/passthrough-vs.glsl';
|
|
import { createShader } from './create-shader';
|
|
import { loadUniform } from './load-uniform';
|
|
|
|
export class FragmentShaderOnlyProgram {
|
|
program: WebGLProgram;
|
|
shaders: Array<WebGLShader> = [];
|
|
|
|
private vao: WebGLVertexArrayObject;
|
|
private uniforms: Array<{
|
|
name: Array<string>;
|
|
location: WebGLUniformLocation;
|
|
type: GLenum;
|
|
}> = [];
|
|
|
|
constructor(
|
|
private gl: WebGL2RenderingContext,
|
|
fragmentShaderSource: string
|
|
) {
|
|
this.createProgram(fragmentShaderSource);
|
|
this.prepareScreenQuad('a_position');
|
|
this.queryUniforms();
|
|
}
|
|
|
|
public bind() {
|
|
this.gl.useProgram(this.program);
|
|
this.gl.bindVertexArray(this.vao);
|
|
}
|
|
|
|
public draw() {
|
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
}
|
|
|
|
public setUniforms(values: any) {
|
|
this.uniforms.forEach(({ name, location, type }) => {
|
|
const value = name.reduce(
|
|
(prev, prop) => (prev !== null && prop in prev ? prev[prop] : null),
|
|
values
|
|
);
|
|
|
|
if (value !== null) {
|
|
loadUniform(this.gl, value, type, location);
|
|
}
|
|
});
|
|
}
|
|
|
|
private queryUniforms() {
|
|
const uniformCount = this.gl.getProgramParameter(
|
|
this.program,
|
|
WebGL2RenderingContext.ACTIVE_UNIFORMS
|
|
);
|
|
|
|
for (let i = 0; i < uniformCount; i++) {
|
|
const glUniform = this.gl.getActiveUniform(this.program, i);
|
|
|
|
this.uniforms.push({
|
|
name: glUniform.name.split(/\[|\]\.|\]|\./).filter((p) => p !== ''),
|
|
location: this.gl.getUniformLocation(this.program, glUniform.name),
|
|
type: glUniform.type,
|
|
});
|
|
}
|
|
}
|
|
|
|
private createProgram(fragmentShaderSource: string) {
|
|
this.program = this.gl.createProgram();
|
|
|
|
const vertexShader = createShader(
|
|
this.gl,
|
|
this.gl.VERTEX_SHADER,
|
|
passthroughVertexShader
|
|
);
|
|
this.gl.attachShader(this.program, vertexShader);
|
|
this.shaders.push(vertexShader);
|
|
|
|
const fragmentShader = createShader(
|
|
this.gl,
|
|
this.gl.FRAGMENT_SHADER,
|
|
fragmentShaderSource
|
|
);
|
|
this.gl.attachShader(this.program, fragmentShader);
|
|
this.shaders.push(fragmentShader);
|
|
|
|
this.gl.linkProgram(this.program);
|
|
|
|
const success = this.gl.getProgramParameter(
|
|
this.program,
|
|
this.gl.LINK_STATUS
|
|
);
|
|
if (!success) {
|
|
throw new Error(this.gl.getProgramInfoLog(this.program));
|
|
}
|
|
}
|
|
|
|
private prepareScreenQuad(attributeName: string) {
|
|
const positionAttributeLocation = this.gl.getAttribLocation(
|
|
this.program,
|
|
attributeName
|
|
);
|
|
|
|
const positionBuffer = this.gl.createBuffer();
|
|
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
|
|
this.gl.bufferData(
|
|
this.gl.ARRAY_BUFFER,
|
|
new Float32Array([-1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]),
|
|
this.gl.STATIC_DRAW
|
|
);
|
|
this.vao = this.gl.createVertexArray();
|
|
|
|
this.gl.bindVertexArray(this.vao);
|
|
this.gl.enableVertexAttribArray(positionAttributeLocation);
|
|
this.gl.vertexAttribPointer(
|
|
positionAttributeLocation,
|
|
2,
|
|
this.gl.FLOAT,
|
|
false,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
}
|