From 163331eea450c38483fe91fee17cd1b63741bbb8 Mon Sep 17 00:00:00 2001 From: monoid Date: Sun, 31 Oct 2021 17:33:10 +0900 Subject: [PATCH] imple phong shader --- index.tsx | 14 ++++---- src/app.ts | 23 +++++++----- src/drawer2D.ts | 9 +---- src/drawer3D.ts | 56 ++++++++++++++++++++--------- src/fragment.frag | 44 ++++++++++++++++++----- src/menu.tsx | 25 +++++++++---- src/phongProgram.ts | 88 +++++++++++++++++++++++++++++++++++++++++++++ src/uniform.ts | 14 ++++---- src/vertex.vert | 4 ++- 9 files changed, 216 insertions(+), 61 deletions(-) create mode 100644 src/phongProgram.ts diff --git a/index.tsx b/index.tsx index 000f78b..7f2d1ff 100644 --- a/index.tsx +++ b/index.tsx @@ -49,15 +49,15 @@ function main(){ console.log("app initialize failed!!!"); return; } - ReactDOM.render({app.renderer.setUniform(u); + ReactDOM.render({app.onChangeUniform(u); }} /> ,document.getElementById("drawer")); - window.addEventListener("resize",(e)=>{ - e.preventDefault(); - canvas.width = document.body.clientWidth; - canvas.height = document.body.clientHeight; - }); + window.addEventListener("resize",(e)=>{ + e.preventDefault(); + canvas.width = document.body.clientWidth; + canvas.height = document.body.clientHeight; + }); canvas.width = document.body.clientWidth; canvas.height = document.body.clientHeight; app.startRun(); diff --git a/src/app.ts b/src/app.ts index 0fd4dd3..a76b711 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,6 @@ import { vec3 } from "gl-matrix"; -import { Drawer2D } from "./drawer2D"; import { Drawer3D } from "./drawer3D"; -import { TriangleDrawer } from "./triangle_drawer"; +import { UniformSet } from "./menu"; class InputMap{ private pressedKey:Set; @@ -39,21 +38,15 @@ const MillisecondPerFrame = 1000/60; export class CanvasApp{ readonly gl: WebGL2RenderingContext; private n : number; - renderer: Drawer2D; - trenderer : TriangleDrawer; - r : Drawer3D; + readonly r : Drawer3D; inputMap: InputMap; constructor(gl: WebGL2RenderingContext){ this.gl = gl; - this.renderer = new Drawer2D(gl); - this.trenderer = new TriangleDrawer(gl); this.r = new Drawer3D(gl); this.inputMap = new InputMap(); this.inputMap.registerHandler(); } intialize():boolean{ - this.renderer.prepare(); - this.trenderer.prepare(this.gl); this.r.init(); let escPressed = false; document.addEventListener("mousemove",(ev)=>{ @@ -121,4 +114,16 @@ export class CanvasApp{ this.r.draw(this.gl,{}); requestAnimationFrame(this.drawScene.bind(this)); } + + onChangeUniform(u:UniformSet){ + this.r.updateUniform(u); + } + getDefaultUniform():UniformSet{ + return { + light_ambientIntensity:0.1, + light_color:[1,1,1], + light_diffuseIntensity:0.5, + light_direction:[0,1,0] + } + } }; \ No newline at end of file diff --git a/src/drawer2D.ts b/src/drawer2D.ts index b09b3c3..445000d 100644 --- a/src/drawer2D.ts +++ b/src/drawer2D.ts @@ -19,11 +19,9 @@ void main() { }`; import { Drawable, RenderState } from "./drawable"; import * as G from "./glWrapper"; -import { getUniformDefaultValue, UniformSet } from "./uniform"; export class Drawer2D implements Drawable{ gl : WebGL2RenderingContext; - uniforms : UniformSet; program: G.GLProgram; vao: G.VertexArray; @@ -31,7 +29,6 @@ export class Drawer2D implements Drawable{ indexBuffer: G.IndexBuffer; constructor(gl: WebGL2RenderingContext){ this.gl = gl; - this.uniforms = getUniformDefaultValue(); try{ this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src)); this.program.use(gl); @@ -74,18 +71,14 @@ export class Drawer2D implements Drawable{ this.indexBuffer = G.createIndexBuffer(gl,index); this.indexBuffer.bind(gl); this.vao.unbind(gl); - this.setUniform(this.uniforms); } useProgram(){ this.program.use(this.gl); } - setUniform(u:UniformSet){ - this.uniforms = u; - } draw(gl:WebGL2RenderingContext, state : RenderState){ this.useProgram(); - this.program.setUniform4fv(gl,"u_color",[this.uniforms.redcolor,0.3,0.8,1.0]); + this.program.setUniform4fv(gl,"u_color",[1.0,0.3,0.8,1.0]); this.vao.bind(gl); gl.drawElements(gl.TRIANGLES,this.indexBuffer.count,gl.UNSIGNED_SHORT,0); diff --git a/src/drawer3D.ts b/src/drawer3D.ts index 8dff96a..25df0ab 100644 --- a/src/drawer3D.ts +++ b/src/drawer3D.ts @@ -1,20 +1,19 @@ - - -/// -import vert_src from "./vertex.vert"; -import frag_src from "./fragment.frag"; - -import {createProgramFromSource, ProgramError, ShaderError} from "./gl_util"; +import { ShaderError} from "./gl_util"; import { Camera } from "./camera"; import * as G from "./glWrapper"; import { Drawable, RenderState } from "./drawable"; import { Model } from "./model"; import { mat4, vec3 } from "gl-matrix"; +import {DirectionalLight, PhongProgram} from "./phongProgram"; +import { UniformSet } from "./menu"; export class Drawer3D implements Drawable{ gl : WebGL2RenderingContext; - program: G.GLProgram; + program: PhongProgram; + + dirLight: DirectionalLight; + camera : Camera; teapot: Model | undefined; cube: Model | undefined; @@ -30,7 +29,7 @@ export class Drawer3D implements Drawable{ this.cube = undefined; this.texture = new G.Texture(gl); try{ - this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src)); + this.program = new PhongProgram(gl); const attr = this.program.getActiveAttributes(gl); console.log(attr); } @@ -40,6 +39,12 @@ export class Drawer3D implements Drawable{ } throw e; } + this.dirLight = { + ambientIntensity: 0.1, + color: [1.0,1.0,1.0], + diffuseIntensity: 0.5, + direction: [0,1,0] + }; } async init(){ const gl = this.gl; @@ -51,35 +56,54 @@ export class Drawer3D implements Drawable{ this.teapot.ready(gl,this.program); this.cube.ready(gl,this.program); - this.texture; const texture_url = new URL("../assets/uv-grid.png",import.meta.url); const texture_image = G.makeImageElement(texture_url.href); texture_image.onload = ()=>{ this.texture.setImage(gl,texture_image); } + this.program.updateDirectionalLight(this.dirLight); } draw(gl: WebGL2RenderingContext,state:RenderState): void { this.camera.aspect = gl.canvas.width/gl.canvas.height; this.camera.UpdateProjectionMat(); this.program.use(gl); - this.program.setUniformMat4f(gl,"viewMat",this.camera.viewMatrix); - this.program.setUniformMat4f(gl,"projectionMat",this.camera.projectionMatrix); - this.texture.bind(gl,0); - this.program.setUniform1i(gl,"mainTexture",0); + this.program.viewMat = this.camera.viewMatrix; + this.program.projectionMat = this.camera.projectionMatrix; + this.program.setUniform3fv(gl,"cameraEyePosition",this.camera.pos); + const colorTextureID = 0; + this.texture.bind(gl,colorTextureID); + this.program.colorTextureID = colorTextureID; if(this.teapot !== undefined){ + this.program.updateMaterial({ + shininess:20, + specularIntensity:1.0 + }); for(const pos of [[0,0,0],[0,0,25],[40,0,25]]){ - this.program.setUniformMat4f(gl,"modelMat",mat4.fromTranslation(mat4.create(), vec3.fromValues(pos[0],pos[1],pos[2]) )); + this.program.modelMat = mat4.fromTranslation(mat4.create(), vec3.fromValues(pos[0],pos[1],pos[2])); this.teapot.draw(gl); } } if(this.cube !== undefined){ + this.program.updateMaterial({ + shininess:20, + specularIntensity:0.5 + }); const modelMat = mat4.create(); mat4.identity(modelMat); mat4.translate(modelMat,modelMat,[45,0,0]); mat4.scale(modelMat,modelMat,[5,5,5]); //mat4.rotateX(modelMat,modelMat,Math.PI * 30 / 180); - this.program.setUniformMat4f(gl,"modelMat",modelMat); + this.program.modelMat = modelMat; this.cube.draw(gl); } } + updateUniform(u:UniformSet){ + this.dirLight = { + ambientIntensity: u.light_ambientIntensity, + diffuseIntensity: u.light_diffuseIntensity, + color: u.light_color, + direction: u.light_direction + }; + this.program.updateDirectionalLight(this.dirLight); + } } \ No newline at end of file diff --git a/src/fragment.frag b/src/fragment.frag index d289497..329c11f 100644 --- a/src/fragment.frag +++ b/src/fragment.frag @@ -1,18 +1,46 @@ #version 300 es precision highp float; -layout(location=0) out vec4 outColor; +struct DirectionalLight { + vec3 color; + float ambientIntensity; + vec3 direction; + float diffuseIntensity; +}; -uniform sampler2D mainTexture; +struct Material{ + float specularIntensity; + float shininess; + sampler2D colorTexture; +}; + +uniform DirectionalLight light; +uniform Material material; +uniform vec3 cameraEyePosition; + +layout(location=0) out vec4 outColor; in vec3 fragNormal; in vec2 texUV; +in vec3 vertexWorldPosition; + +vec3 calcDiffuse(){ + float factor = dot(light.direction,normalize(fragNormal)); + factor = max(factor,0.0); + return light.color * light.diffuseIntensity * factor; +} +vec3 calcSpecular(){ + vec3 v = normalize(cameraEyePosition - vertexWorldPosition); + vec3 n = 2.0 * normalize(fragNormal) *dot(fragNormal,light.direction) - light.direction; + float cosineRV = max(dot(v,n),0.0); + return pow(cosineRV, material.shininess) * light.color * material.specularIntensity; +} void main() { - vec3 lightPos = vec3(20,20,1); - vec3 c = normalize(lightPos); - float intense = dot(c,normalize(fragNormal)); - intense = (max(intense,-0.5)+1.0)/2.0; - vec4 objectColor = texture(mainTexture,texUV); - outColor = vec4(objectColor.xyz * intense,1); + vec3 lightAmbient = light.color * light.ambientIntensity; + vec3 lightDiffuse = calcDiffuse(); + vec3 lightSpecular = calcSpecular(); + vec4 materialColor = texture(material.colorTexture,texUV); + materialColor *= vec4(lightAmbient + lightDiffuse + lightSpecular,1.0); + outColor = vec4(materialColor.xyz,1.0); } \ No newline at end of file diff --git a/src/menu.tsx b/src/menu.tsx index 5b7a09f..4459530 100644 --- a/src/menu.tsx +++ b/src/menu.tsx @@ -1,19 +1,32 @@ import * as React from 'react'; import Slider from "@mui/material/Slider"; import Typograpy from "@mui/material/Typography"; -import {UniformSet} from "./uniform"; +import { vec3 } from 'gl-matrix'; + +export type UniformSet = { + light_color: vec3, + light_ambientIntensity:number, + light_direction: vec3, + light_diffuseIntensity:number, +}; export function MenuBar(prop:{u:UniformSet,onUniformChange:(u:UniformSet)=>void}){ const [uniform,setUniform] = React.useState(prop.u); - const onRedChange = (_,v)=>{ - setUniform({...uniform,redcolor:v/100}); + const onDiffIntenseChange = (_,v)=>{ + setUniform({...uniform,light_diffuseIntensity:v/100}); + }; + const onAmbientIntenseChange = (_,v)=>{ + setUniform({...uniform,light_ambientIntensity:v/100}); }; React.useEffect(()=>{ prop.onUniformChange(uniform); }); - const a = uniform.redcolor*100; + const diffuseIntense = uniform.light_diffuseIntensity*100; + const ambientIntense = uniform.light_ambientIntensity*100; return (
- Uniform - + Light Diffuse Intense + + Light Ambient Intense +
); } \ No newline at end of file diff --git a/src/phongProgram.ts b/src/phongProgram.ts new file mode 100644 index 0000000..f218e16 --- /dev/null +++ b/src/phongProgram.ts @@ -0,0 +1,88 @@ +/// +import vert_src from "./vertex.vert"; +import frag_src from "./fragment.frag"; + +import {createProgramFromSource, ProgramError, ShaderError} from "./gl_util"; +import { Camera } from "./camera"; +import * as G from "./glWrapper"; +import { Drawable, RenderState } from "./drawable"; +import { Model } from "./model"; +import { mat4, vec3 } from "gl-matrix"; + +export interface DirectionalLight{ + color : vec3; + ambientIntensity : number; + direction : vec3; + diffuseIntensity : number; +} +export interface Material{ + specularIntensity: number; + shininess: number; +} + +export class PhongProgram extends G.GLProgram{ + gl: WebGL2RenderingContext; + constructor(gl: WebGL2RenderingContext){ + super(createProgramFromSource(gl,vert_src,frag_src)); + this.gl = gl; + this._viewMat = mat4.create(); + this._projectionMat = mat4.create(); + this._modelMat = mat4.create(); + } + + private _viewMat : mat4; + public get viewMat() : mat4 { + return this._viewMat; + } + public set viewMat(v : mat4) { + if(!mat4.equals(v,this._viewMat)){ + this.setUniformMat4f(this.gl,"viewMat",v); + this._viewMat = v; + } + } + + private _projectionMat : mat4; + public get projectionMat() : mat4 { + return this._projectionMat; + } + public set projectionMat(v : mat4) { + if(!mat4.equals(v,this._projectionMat)){ + this.setUniformMat4f(this.gl,"projectionMat",v); + this._projectionMat = v; + } + } + + private _modelMat : mat4; + public get modelMat() : mat4 { + return this._modelMat; + } + public set modelMat(v : mat4) { + if(!mat4.equals(v,this._modelMat)){ + this.setUniformMat4f(this.gl,"modelMat",v); + this._modelMat = v; + } + } + + + private _colorTextureID : number; + public get colorTextureID() : number { + return this._colorTextureID; + } + public set colorTextureID(v : number) { + if(v !== this._colorTextureID){ + this.setUniform1i(this.gl,"material.colorTexture",v); + this._colorTextureID = v; + } + } + + updateDirectionalLight(light: DirectionalLight){ + this.setUniform3fv(this.gl,"light.color",light.color); + this.setUniform1f(this.gl,"light.ambientIntensity",light.ambientIntensity); + this.setUniform3fv(this.gl,"light.direction",light.direction); + this.setUniform1f(this.gl,"light.diffuseIntensity",light.diffuseIntensity); + } + updateMaterial(material: Material){ + this.setUniform1f(this.gl,"material.shininess",material.shininess); + this.setUniform1f(this.gl,"material.specularIntensity",material.specularIntensity); + } +} \ No newline at end of file diff --git a/src/uniform.ts b/src/uniform.ts index ad65653..f44227f 100644 --- a/src/uniform.ts +++ b/src/uniform.ts @@ -1,9 +1,11 @@ -export type UniformSet = { - redcolor:number -}; +import { DirectionalLight } from "./phongProgram"; +import { UniformSet } from "./menu"; -export function getUniformDefaultValue():UniformSet{ +export function dirLightFromMenuParam(u: UniformSet): DirectionalLight{ return { - redcolor:0.5, - } + ambientIntensity: u.light_ambientIntensity, + diffuseIntensity: u.light_diffuseIntensity, + color: u.light_color, + direction: u.light_direction + }; } \ No newline at end of file diff --git a/src/vertex.vert b/src/vertex.vert index 626b745..71deda7 100644 --- a/src/vertex.vert +++ b/src/vertex.vert @@ -1,5 +1,6 @@ #version 300 es + layout(location=0) in vec3 position; layout(location=1) in vec2 textureUV; layout(location=2) in vec3 normal; @@ -10,10 +11,11 @@ uniform mat4 projectionMat; out vec3 fragNormal; out vec2 texUV; - +out vec3 vertexWorldPosition; void main() { gl_Position = projectionMat * viewMat * modelMat * vec4(position,1); fragNormal = mat3(transpose(inverse(modelMat))) * normal; + vertexWorldPosition = (modelMat * vec4(position,1.0)).xyz; texUV = textureUV; } \ No newline at end of file