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 { WebGLApp } from "../renderer/webgl";
import { Renderable } from "../renderer/renderable";
import { Transform } from "../renderer/transform";
import { Transform, etoq, v3 } from "../renderer/transform";
import torus from "../meshes/torus";
import { errorShader } from "../common-shaders/error";
import plane from "../meshes/plane";
import { uvRainbow } from "../common-shaders/uv-rainbow";
import uvsphere from "../meshes/uvsphere";
import { quat } from "gl-matrix";
import { mat4, quat } from "gl-matrix";
import trianglething from "../meshes/trianglething";
import teapot from "../meshes/teapot";
import { basic } from "../common-shaders/basic";
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(
app,
new Transform(),
new MeshRenderer(app, trianglething, uvRainbow(app), camera).configure({})
transform,
new MeshRenderer(app, teapot, basic(app), camera, light).configure({})
);
// new Renderable(
// app,
// new Transform(),
// new MeshRenderer(app, torus, uvRainbow(app), camera).configure({})
// );
app.start();

View file

@ -1,6 +1,42 @@
#version 300 es
precision highp float;
uniform float u_time;
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() {
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
}
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";
export const basicShaderConfig: ShaderConfig = {
model: 0,
view: 4,
projection: 8,
world: 12,
light0: 13,
light0Color: 17,
uv0: 18,
normals: 19,
vertexColor: 20,
time: 21,
attributes: {
vertex: "a_vertex",
uv0: "a_uv0",
normal: "a_normal",
vertexColor: "a_vertex_color",
},
uniforms: {
view: "u_view",
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) =>

View file

@ -1,17 +1,33 @@
#version 300 es
layout(location = 0) in mat4 model;
layout(location = 4) in mat4 view;
layout(location = 8) in mat4 projection;
layout(location = 12) in vec3 worldPos;
layout(location = 13) in mat4 light0;
layout(location = 17) in vec4 light0Color;
layout(location = 18) in vec2 uv0;
layout(location = 19) in vec3 normals;
layout(location = 20) in vec4 vertexColor;
layout(location = 21) in float time;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform mat4 u_object_to_world;
uniform mat4 u_object_to_world_inv;
uniform vec3 u_light_0;
uniform vec4 u_light_0_color;
uniform float u_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() {
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 { Mesh } from "./mesh";
import { Shader } from "./shader";
import { Shader, ShaderMapping } from "./shader";
import { WebGLApp } from "./webgl";
import { Transform } from "./transform";
import { Transform, v3 } from "./transform";
export type MeshRendererConfig = {
drawMode?: number;
cullMode?: number;
meshTransform?: mat4; // Do not use this for per-frame shit. Just the model pre-transform.
};
export class MeshRenderer extends Behavior {
@ -26,6 +27,7 @@ export class MeshRenderer extends Behavior {
public mesh: Mesh,
public shader: Shader,
public camera: Transform = new Transform([0, 0, -6]),
public light: Transform = new Transform([100, 100, 0]),
public config: MeshRendererConfig = {}
) {
super(app);
@ -56,14 +58,21 @@ export class MeshRenderer extends Behavior {
this.app.gl.ELEMENT_ARRAY_BUFFER
);
const shaderMap = this.shader.mappings;
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) {
this.buffers.normal = this.makeBuffer(this.mesh.config.normals);
this.bindAttrib(
this.buffers.normal,
"aVertexNormals",
shaderMap.attributes.normal,
3,
this.app.gl.FLOAT,
true
@ -74,7 +83,7 @@ export class MeshRenderer extends Behavior {
this.buffers.color = this.makeBuffer(this.mesh.config.colors);
this.bindAttrib(
this.buffers.color,
"aVertexColors",
shaderMap.attributes.vertexColor,
4,
this.app.gl.UNSIGNED_BYTE
);
@ -82,7 +91,12 @@ export class MeshRenderer extends Behavior {
if (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);
}
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() {
mat4.perspective(
this.projectionMatrix,
@ -124,6 +158,7 @@ export class MeshRenderer extends Behavior {
);
this.shader.compile();
this.shader.link();
this.initializeBuffers();
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.ARRAY_BUFFER, this.buffers.position || null);
this.shader.use();
this.shader.setupUniforms(
time,
this.projectionMatrix,
transform,
this.camera
);
this.initializeShader(time, transform);
gl.drawElements(
this.config.drawMode ?? gl.TRIANGLES,

View file

@ -5,35 +5,46 @@ import { WebGLApp } from "./webgl";
/**
* Uniform/Attribute locations
*/
export type ShaderConfig = {
// Engine rendering features
model?: 0; // always zero to ensure we render correctly. This is implied.
view?: number;
projection?: number;
world?: number;
light0?: number;
light0Color?: number;
uv0?: number;
normals?: number;
vertexColor?: number;
time?: number;
export type ShaderConfig = ShaderMapping<string, string>;
export type InternalMapping = ShaderMapping<
number,
WebGLUniformLocation | null
>;
// other reasons (like materials)
material?: { [key: string]: number };
export type ShaderMapping<
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 {
static VERTEX = 35633;
static FRAGMENT = 35632;
constructor(private config: ShaderConfig) {
config.model = 0;
}
constructor(public config: ShaderConfig) {}
private vertexCode = "";
private fragmentCode = "";
private _app?: WebGLApp;
private _program: WebGLProgram | null = null;
public program: WebGLProgram | null = null;
public mappings: InternalMapping = {} as any;
get gl() {
const gl = this._app?.gl;
@ -46,7 +57,7 @@ export class Shader {
app(app: WebGLApp) {
this._app = app;
this._program = app.gl.createProgram();
this.program = app.gl.createProgram();
return this;
}
@ -62,11 +73,31 @@ export class Shader {
}
attrib(name: string) {
return this.gl.getAttribLocation(this._program as WebGLProgram, name);
return this.gl.getAttribLocation(this.program as WebGLProgram, name);
}
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) {
@ -79,7 +110,7 @@ export class Shader {
gl.shaderSource(shader, source);
gl.compileShader(shader);
gl.attachShader(this._program as WebGLProgram, shader);
gl.attachShader(this.program as WebGLProgram, shader);
}
compile() {
@ -89,54 +120,31 @@ export class Shader {
}
link() {
this.gl.linkProgram(this._program as WebGLProgram);
this.gl.linkProgram(this.program as WebGLProgram);
if (
!this.gl.getProgramParameter(
this._program as WebGLProgram,
this.program as WebGLProgram,
this.gl.LINK_STATUS
)
) {
throw new Error(
"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) {
this.gl.bindAttribLocation(
this._program as WebGLProgram,
this.program as WebGLProgram,
attribLocation,
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() {
this._app?.gl.useProgram(this._program);
this._app?.gl.useProgram(this.program);
}
}

View file

@ -23,3 +23,8 @@ export class Transform {
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];