basic lighting

This commit is contained in:
41666 2023-10-11 23:19:27 -04:00
parent d5025fabac
commit 74135870d0
11 changed files with 14450 additions and 2122 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,30 +1,32 @@
import { MeshRenderer } from "../renderer/mesh-renderer"; import { MeshRenderer } from "../renderer/mesh-renderer";
import { WebGLApp } from "../renderer/webgl"; import { WebGLApp } from "../renderer/webgl";
import { Renderable } from "../renderer/renderable"; import { Renderable } from "../renderer/renderable";
import { Transform } from "../renderer/transform"; import { Transform, etoq, v3 } from "../renderer/transform";
import torus from "../meshes/torus"; import torus from "../meshes/torus";
import { errorShader } from "../common-shaders/error"; import { errorShader } from "../common-shaders/error";
import plane from "../meshes/plane"; import plane from "../meshes/plane";
import { uvRainbow } from "../common-shaders/uv-rainbow"; import { uvRainbow } from "../common-shaders/uv-rainbow";
import uvsphere from "../meshes/uvsphere"; import uvsphere from "../meshes/uvsphere";
import { quat } from "gl-matrix"; import { mat4, quat } from "gl-matrix";
import trianglething from "../meshes/trianglething"; import trianglething from "../meshes/trianglething";
import teapot from "../meshes/teapot"; import teapot from "../meshes/teapot";
import { basic } from "../common-shaders/basic";
const app = new WebGLApp({ fov: 45 }); const app = new WebGLApp({ fov: 45 });
const camera = new Transform([0, -2, -6]); const camera = new Transform([3, 3, 5], etoq([-15, 26, 0]));
const light = new Transform([1, 1, 0]);
const transform = new Transform(v3(0), etoq([0, 0, 0]));
app.onUpdate(() => {
quat.rotateY(transform.rotation, transform.rotation, 0.001);
});
new Renderable( new Renderable(
app, app,
new Transform(), transform,
new MeshRenderer(app, trianglething, uvRainbow(app), camera).configure({}) new MeshRenderer(app, teapot, basic(app), camera, light).configure({})
); );
// new Renderable(
// app,
// new Transform(),
// new MeshRenderer(app, torus, uvRainbow(app), camera).configure({})
// );
app.start(); app.start();

View file

@ -1,6 +1,42 @@
#version 300 es #version 300 es
precision highp float; precision highp float;
void main() { uniform float u_time;
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
in vec2 uv0;
in vec3 normal;
in vec4 vertex_color;
in vec3 light_pos;
out vec4 fragColor;
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
vec3 normal = normalize(normal);
float light = dot(normal, light_pos);
float zComponent = sin(u_time) * 0.004 * 0.5 + 0.5;
vec3 hsv = rgb2hsv(vec3(uv0, zComponent));
hsv.x += u_time * 0.0004;
hsv.y = 1.0;
hsv.z = 1.0;
vec3 rgb = hsv2rgb(hsv);
fragColor = vec4(rgb, 1.0);
fragColor *= max(light, 0.1);
} }

View file

@ -4,16 +4,22 @@ import frag from "./basic.frag";
import vert from "./basic.vert"; import vert from "./basic.vert";
export const basicShaderConfig: ShaderConfig = { export const basicShaderConfig: ShaderConfig = {
model: 0, attributes: {
view: 4, vertex: "a_vertex",
projection: 8, uv0: "a_uv0",
world: 12, normal: "a_normal",
light0: 13, vertexColor: "a_vertex_color",
light0Color: 17, },
uv0: 18,
normals: 19, uniforms: {
vertexColor: 20, view: "u_view",
time: 21, projection: "u_projection",
objectToWorld: "u_object_to_world",
objectToWorldInv: "u_object_to_world_inv",
light0: "u_light_0",
light0Color: "u_light_0_color",
time: "u_time",
},
}; };
export const basic = (app: WebGLApp) => export const basic = (app: WebGLApp) =>

View file

@ -1,17 +1,33 @@
#version 300 es #version 300 es
layout(location = 0) in mat4 model; uniform mat4 u_view;
layout(location = 4) in mat4 view; uniform mat4 u_projection;
layout(location = 8) in mat4 projection; uniform mat4 u_object_to_world;
layout(location = 12) in vec3 worldPos; uniform mat4 u_object_to_world_inv;
layout(location = 13) in mat4 light0; uniform vec3 u_light_0;
layout(location = 17) in vec4 light0Color; uniform vec4 u_light_0_color;
layout(location = 18) in vec2 uv0; uniform float u_time;
layout(location = 19) in vec3 normals;
layout(location = 20) in vec4 vertexColor;
layout(location = 21) in float time;
// injection point in vec4 a_vertex;
in vec2 a_uv0;
in vec3 a_normal;
in vec4 a_vertex_color;
out vec2 uv0;
out vec3 normal;
out vec4 vertex_color;
out vec3 light_pos;
// injection point 1
void main() { void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; mat4 worldInv = inverse(u_view);
mat4 MVW = u_projection * u_view * u_object_to_world;
gl_Position = MVW * a_vertex;
uv0 = a_uv0;
normal = normalize(mat3(worldInv) * a_normal);
vertex_color = a_vertex_color;
light_pos = normalize(mat3(u_object_to_world_inv) * u_light_0);
// injection point 2
} }

View file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,14 @@
import { mat4, vec3 } from "gl-matrix"; import { mat4, quat, vec3 } from "gl-matrix";
import { Behavior } from "./behavior"; import { Behavior } from "./behavior";
import { Mesh } from "./mesh"; import { Mesh } from "./mesh";
import { Shader } from "./shader"; import { Shader, ShaderMapping } from "./shader";
import { WebGLApp } from "./webgl"; import { WebGLApp } from "./webgl";
import { Transform } from "./transform"; import { Transform, v3 } from "./transform";
export type MeshRendererConfig = { export type MeshRendererConfig = {
drawMode?: number; drawMode?: number;
cullMode?: number; cullMode?: number;
meshTransform?: mat4; // Do not use this for per-frame shit. Just the model pre-transform.
}; };
export class MeshRenderer extends Behavior { export class MeshRenderer extends Behavior {
@ -26,6 +27,7 @@ export class MeshRenderer extends Behavior {
public mesh: Mesh, public mesh: Mesh,
public shader: Shader, public shader: Shader,
public camera: Transform = new Transform([0, 0, -6]), public camera: Transform = new Transform([0, 0, -6]),
public light: Transform = new Transform([100, 100, 0]),
public config: MeshRendererConfig = {} public config: MeshRendererConfig = {}
) { ) {
super(app); super(app);
@ -56,14 +58,21 @@ export class MeshRenderer extends Behavior {
this.app.gl.ELEMENT_ARRAY_BUFFER this.app.gl.ELEMENT_ARRAY_BUFFER
); );
const shaderMap = this.shader.mappings;
this.buffers.position = this.makeBuffer(this.mesh.config.positions); this.buffers.position = this.makeBuffer(this.mesh.config.positions);
this.bindAttrib(this.buffers.position, 0, 3, this.app.gl.FLOAT); this.bindAttrib(
this.buffers.position,
shaderMap.attributes.vertex,
3,
this.app.gl.FLOAT
);
if (this.mesh.config.normals) { if (this.mesh.config.normals) {
this.buffers.normal = this.makeBuffer(this.mesh.config.normals); this.buffers.normal = this.makeBuffer(this.mesh.config.normals);
this.bindAttrib( this.bindAttrib(
this.buffers.normal, this.buffers.normal,
"aVertexNormals", shaderMap.attributes.normal,
3, 3,
this.app.gl.FLOAT, this.app.gl.FLOAT,
true true
@ -74,7 +83,7 @@ export class MeshRenderer extends Behavior {
this.buffers.color = this.makeBuffer(this.mesh.config.colors); this.buffers.color = this.makeBuffer(this.mesh.config.colors);
this.bindAttrib( this.bindAttrib(
this.buffers.color, this.buffers.color,
"aVertexColors", shaderMap.attributes.vertexColor,
4, 4,
this.app.gl.UNSIGNED_BYTE this.app.gl.UNSIGNED_BYTE
); );
@ -82,7 +91,12 @@ export class MeshRenderer extends Behavior {
if (this.mesh.config.uvs) { if (this.mesh.config.uvs) {
this.buffers.uv = this.makeBuffer(this.mesh.config.uvs); this.buffers.uv = this.makeBuffer(this.mesh.config.uvs);
this.bindAttrib(this.buffers.uv, 1, 2, this.app.gl.FLOAT); this.bindAttrib(
this.buffers.uv,
shaderMap.attributes.uv0,
2,
this.app.gl.FLOAT
);
} }
} }
@ -114,6 +128,26 @@ export class MeshRenderer extends Behavior {
gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindBuffer(gl.ARRAY_BUFFER, null);
} }
initializeShader(time: number, transform: Transform) {
const view = mat4.invert(mat4.create(), this.camera.toMat4());
const gl = this.app.gl;
const { uniforms } = this.shader.mappings;
this.shader.use();
gl.uniform1f(uniforms.time, time);
gl.uniform4fv(uniforms.light0Color, [1, 1, 1, 1]);
gl.uniformMatrix4fv(uniforms.view, false, view);
gl.uniformMatrix4fv(uniforms.projection, false, this.projectionMatrix);
gl.uniform3fv(uniforms.light0, this.light.position);
gl.uniformMatrix4fv(uniforms.objectToWorld, false, transform.toMat4());
gl.uniformMatrix4fv(
uniforms.objectToWorldInv,
false,
mat4.invert(mat4.create(), transform.toMat4())
);
}
onStart() { onStart() {
mat4.perspective( mat4.perspective(
this.projectionMatrix, this.projectionMatrix,
@ -124,6 +158,7 @@ export class MeshRenderer extends Behavior {
); );
this.shader.compile(); this.shader.compile();
this.shader.link();
this.initializeBuffers(); this.initializeBuffers();
this.shader.link(); this.shader.link();
} }
@ -134,14 +169,7 @@ export class MeshRenderer extends Behavior {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.faces || null); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.faces || null);
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.position || null); gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.position || null);
this.shader.use(); this.initializeShader(time, transform);
this.shader.setupUniforms(
time,
this.projectionMatrix,
transform,
this.camera
);
gl.drawElements( gl.drawElements(
this.config.drawMode ?? gl.TRIANGLES, this.config.drawMode ?? gl.TRIANGLES,

View file

@ -5,35 +5,46 @@ import { WebGLApp } from "./webgl";
/** /**
* Uniform/Attribute locations * Uniform/Attribute locations
*/ */
export type ShaderConfig = { export type ShaderConfig = ShaderMapping<string, string>;
// Engine rendering features export type InternalMapping = ShaderMapping<
model?: 0; // always zero to ensure we render correctly. This is implied. number,
view?: number; WebGLUniformLocation | null
projection?: number; >;
world?: number;
light0?: number;
light0Color?: number;
uv0?: number;
normals?: number;
vertexColor?: number;
time?: number;
// other reasons (like materials) export type ShaderMapping<
material?: { [key: string]: number }; Attr extends number | string,
Uniform extends (WebGLUniformLocation | null) | string,
> = {
attributes: {
vertex: Attr;
uv0: Attr;
normal: Attr;
vertexColor: Attr;
material?: { [key: string]: Attr };
};
uniforms: {
view: Uniform;
projection: Uniform;
objectToWorld: Uniform;
objectToWorldInv: Uniform;
light0: Uniform;
light0Color: Uniform;
time: Uniform;
};
}; };
export class Shader { export class Shader {
static VERTEX = 35633; static VERTEX = 35633;
static FRAGMENT = 35632; static FRAGMENT = 35632;
constructor(private config: ShaderConfig) { constructor(public config: ShaderConfig) {}
config.model = 0;
}
private vertexCode = ""; private vertexCode = "";
private fragmentCode = ""; private fragmentCode = "";
private _app?: WebGLApp; private _app?: WebGLApp;
private _program: WebGLProgram | null = null; public program: WebGLProgram | null = null;
public mappings: InternalMapping = {} as any;
get gl() { get gl() {
const gl = this._app?.gl; const gl = this._app?.gl;
@ -46,7 +57,7 @@ export class Shader {
app(app: WebGLApp) { app(app: WebGLApp) {
this._app = app; this._app = app;
this._program = app.gl.createProgram(); this.program = app.gl.createProgram();
return this; return this;
} }
@ -62,11 +73,31 @@ export class Shader {
} }
attrib(name: string) { attrib(name: string) {
return this.gl.getAttribLocation(this._program as WebGLProgram, name); return this.gl.getAttribLocation(this.program as WebGLProgram, name);
} }
uniform(name: string) { uniform(name: string) {
return this.gl.getUniformLocation(this._program as WebGLProgram, name); return this.gl.getUniformLocation(this.program as WebGLProgram, name);
}
generateMappings(config: ShaderConfig): InternalMapping {
return {
attributes: {
normal: this.attrib(config.attributes.normal),
uv0: this.attrib(config.attributes.uv0),
vertex: this.attrib(config.attributes.vertex),
vertexColor: this.attrib(config.attributes.vertexColor),
},
uniforms: {
light0: this.uniform(config.uniforms.light0),
light0Color: this.uniform(config.uniforms.light0Color),
objectToWorld: this.uniform(config.uniforms.objectToWorld),
objectToWorldInv: this.uniform(config.uniforms.objectToWorldInv),
projection: this.uniform(config.uniforms.projection),
view: this.uniform(config.uniforms.view),
time: this.uniform(config.uniforms.time),
},
};
} }
attach(which: number, source: string) { attach(which: number, source: string) {
@ -79,7 +110,7 @@ export class Shader {
gl.shaderSource(shader, source); gl.shaderSource(shader, source);
gl.compileShader(shader); gl.compileShader(shader);
gl.attachShader(this._program as WebGLProgram, shader); gl.attachShader(this.program as WebGLProgram, shader);
} }
compile() { compile() {
@ -89,54 +120,31 @@ export class Shader {
} }
link() { link() {
this.gl.linkProgram(this._program as WebGLProgram); this.gl.linkProgram(this.program as WebGLProgram);
if ( if (
!this.gl.getProgramParameter( !this.gl.getProgramParameter(
this._program as WebGLProgram, this.program as WebGLProgram,
this.gl.LINK_STATUS this.gl.LINK_STATUS
) )
) { ) {
throw new Error( throw new Error(
"Unable to initialize the shader program: " + "Unable to initialize the shader program: " +
this.gl.getProgramInfoLog(this._program as WebGLProgram) this.gl.getProgramInfoLog(this.program as WebGLProgram)
); );
} }
this.mappings = this.generateMappings(this.config);
} }
bindAttrib(attribLocation: number, name: string) { bindAttrib(attribLocation: number, name: string) {
this.gl.bindAttribLocation( this.gl.bindAttribLocation(
this._program as WebGLProgram, this.program as WebGLProgram,
attribLocation, attribLocation,
name name
); );
} }
setupUniforms(
time: number,
projection: mat4,
model: Transform,
view: Transform
) {
const viewMatrix = view.toMat4();
mat4.invert(viewMatrix, viewMatrix);
const { gl } = this._app as WebGLApp;
gl.useProgram(this._program as WebGLProgram);
gl.uniformMatrix4fv(this.uniform("uProjectionMatrix"), false, projection);
if (this.config.time) {
gl.uniform1f(this.uniform("uTime"), time);
}
const modelMat = mat4.clone(model.toMat4());
mat4.fromQuat(modelMat, view.rotation);
mat4.translate(modelMat, modelMat, view.position);
gl.uniformMatrix4fv(this.uniform("uModelViewMatrix"), false, modelMat);
}
use() { use() {
this._app?.gl.useProgram(this._program); this._app?.gl.useProgram(this.program);
} }
} }

View file

@ -23,3 +23,8 @@ export class Transform {
return mat; return mat;
} }
} }
export const etoq = (v3: vec3): quat =>
quat.fromEuler(quat.create(), v3[0], v3[1], v3[2]);
export const v3 = (v: number): vec3 => [v, v, v];