basic lighting
This commit is contained in:
parent
d5025fabac
commit
74135870d0
11 changed files with 14450 additions and 2122 deletions
File diff suppressed because one or more lines are too long
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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
|
||||
}
|
0
src/meshes/note-UP_is_y--FWD_is_x.txt
Normal file
0
src/meshes/note-UP_is_y--FWD_is_x.txt
Normal file
16253
src/meshes/teapot.ply
16253
src/meshes/teapot.ply
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Add table
Reference in a new issue