imple phong shader

This commit is contained in:
monoid 2021-10-31 17:33:10 +09:00
parent 1202b4faad
commit 163331eea4
9 changed files with 216 additions and 61 deletions

View File

@ -49,8 +49,8 @@ function main(){
console.log("app initialize failed!!!"); console.log("app initialize failed!!!");
return; return;
} }
ReactDOM.render(<MenuBar u={app.renderer.uniforms} ReactDOM.render(<MenuBar u={app.getDefaultUniform()}
onUniformChange={(u)=>{app.renderer.setUniform(u); onUniformChange={(u)=>{app.onChangeUniform(u);
}} /> }} />
,document.getElementById("drawer")); ,document.getElementById("drawer"));
window.addEventListener("resize",(e)=>{ window.addEventListener("resize",(e)=>{

View File

@ -1,7 +1,6 @@
import { vec3 } from "gl-matrix"; import { vec3 } from "gl-matrix";
import { Drawer2D } from "./drawer2D";
import { Drawer3D } from "./drawer3D"; import { Drawer3D } from "./drawer3D";
import { TriangleDrawer } from "./triangle_drawer"; import { UniformSet } from "./menu";
class InputMap{ class InputMap{
private pressedKey:Set<string>; private pressedKey:Set<string>;
@ -39,21 +38,15 @@ const MillisecondPerFrame = 1000/60;
export class CanvasApp{ export class CanvasApp{
readonly gl: WebGL2RenderingContext; readonly gl: WebGL2RenderingContext;
private n : number; private n : number;
renderer: Drawer2D; readonly r : Drawer3D;
trenderer : TriangleDrawer;
r : Drawer3D;
inputMap: InputMap; inputMap: InputMap;
constructor(gl: WebGL2RenderingContext){ constructor(gl: WebGL2RenderingContext){
this.gl = gl; this.gl = gl;
this.renderer = new Drawer2D(gl);
this.trenderer = new TriangleDrawer(gl);
this.r = new Drawer3D(gl); this.r = new Drawer3D(gl);
this.inputMap = new InputMap(); this.inputMap = new InputMap();
this.inputMap.registerHandler(); this.inputMap.registerHandler();
} }
intialize():boolean{ intialize():boolean{
this.renderer.prepare();
this.trenderer.prepare(this.gl);
this.r.init(); this.r.init();
let escPressed = false; let escPressed = false;
document.addEventListener("mousemove",(ev)=>{ document.addEventListener("mousemove",(ev)=>{
@ -121,4 +114,16 @@ export class CanvasApp{
this.r.draw(this.gl,{}); this.r.draw(this.gl,{});
requestAnimationFrame(this.drawScene.bind(this)); 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]
}
}
}; };

View File

@ -19,11 +19,9 @@ void main() {
}`; }`;
import { Drawable, RenderState } from "./drawable"; import { Drawable, RenderState } from "./drawable";
import * as G from "./glWrapper"; import * as G from "./glWrapper";
import { getUniformDefaultValue, UniformSet } from "./uniform";
export class Drawer2D implements Drawable{ export class Drawer2D implements Drawable{
gl : WebGL2RenderingContext; gl : WebGL2RenderingContext;
uniforms : UniformSet;
program: G.GLProgram; program: G.GLProgram;
vao: G.VertexArray; vao: G.VertexArray;
@ -31,7 +29,6 @@ export class Drawer2D implements Drawable{
indexBuffer: G.IndexBuffer; indexBuffer: G.IndexBuffer;
constructor(gl: WebGL2RenderingContext){ constructor(gl: WebGL2RenderingContext){
this.gl = gl; this.gl = gl;
this.uniforms = getUniformDefaultValue();
try{ try{
this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src)); this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src));
this.program.use(gl); this.program.use(gl);
@ -74,18 +71,14 @@ export class Drawer2D implements Drawable{
this.indexBuffer = G.createIndexBuffer(gl,index); this.indexBuffer = G.createIndexBuffer(gl,index);
this.indexBuffer.bind(gl); this.indexBuffer.bind(gl);
this.vao.unbind(gl); this.vao.unbind(gl);
this.setUniform(this.uniforms);
} }
useProgram(){ useProgram(){
this.program.use(this.gl); this.program.use(this.gl);
} }
setUniform(u:UniformSet){
this.uniforms = u;
}
draw(gl:WebGL2RenderingContext, state : RenderState){ draw(gl:WebGL2RenderingContext, state : RenderState){
this.useProgram(); 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); this.vao.bind(gl);
gl.drawElements(gl.TRIANGLES,this.indexBuffer.count,gl.UNSIGNED_SHORT,0); gl.drawElements(gl.TRIANGLES,this.indexBuffer.count,gl.UNSIGNED_SHORT,0);

View File

@ -1,20 +1,19 @@
import { ShaderError} from "./gl_util";
/// <reference path="./glsl.d.ts" />
import vert_src from "./vertex.vert";
import frag_src from "./fragment.frag";
import {createProgramFromSource, ProgramError, ShaderError} from "./gl_util";
import { Camera } from "./camera"; import { Camera } from "./camera";
import * as G from "./glWrapper"; import * as G from "./glWrapper";
import { Drawable, RenderState } from "./drawable"; import { Drawable, RenderState } from "./drawable";
import { Model } from "./model"; import { Model } from "./model";
import { mat4, vec3 } from "gl-matrix"; import { mat4, vec3 } from "gl-matrix";
import {DirectionalLight, PhongProgram} from "./phongProgram";
import { UniformSet } from "./menu";
export class Drawer3D implements Drawable{ export class Drawer3D implements Drawable{
gl : WebGL2RenderingContext; gl : WebGL2RenderingContext;
program: G.GLProgram; program: PhongProgram;
dirLight: DirectionalLight;
camera : Camera; camera : Camera;
teapot: Model | undefined; teapot: Model | undefined;
cube: Model | undefined; cube: Model | undefined;
@ -30,7 +29,7 @@ export class Drawer3D implements Drawable{
this.cube = undefined; this.cube = undefined;
this.texture = new G.Texture(gl); this.texture = new G.Texture(gl);
try{ try{
this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src)); this.program = new PhongProgram(gl);
const attr = this.program.getActiveAttributes(gl); const attr = this.program.getActiveAttributes(gl);
console.log(attr); console.log(attr);
} }
@ -40,6 +39,12 @@ export class Drawer3D implements Drawable{
} }
throw e; throw e;
} }
this.dirLight = {
ambientIntensity: 0.1,
color: [1.0,1.0,1.0],
diffuseIntensity: 0.5,
direction: [0,1,0]
};
} }
async init(){ async init(){
const gl = this.gl; const gl = this.gl;
@ -51,35 +56,54 @@ export class Drawer3D implements Drawable{
this.teapot.ready(gl,this.program); this.teapot.ready(gl,this.program);
this.cube.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_url = new URL("../assets/uv-grid.png",import.meta.url);
const texture_image = G.makeImageElement(texture_url.href); const texture_image = G.makeImageElement(texture_url.href);
texture_image.onload = ()=>{ texture_image.onload = ()=>{
this.texture.setImage(gl,texture_image); this.texture.setImage(gl,texture_image);
} }
this.program.updateDirectionalLight(this.dirLight);
} }
draw(gl: WebGL2RenderingContext,state:RenderState): void { draw(gl: WebGL2RenderingContext,state:RenderState): void {
this.camera.aspect = gl.canvas.width/gl.canvas.height; this.camera.aspect = gl.canvas.width/gl.canvas.height;
this.camera.UpdateProjectionMat(); this.camera.UpdateProjectionMat();
this.program.use(gl); this.program.use(gl);
this.program.setUniformMat4f(gl,"viewMat",this.camera.viewMatrix); this.program.viewMat = this.camera.viewMatrix;
this.program.setUniformMat4f(gl,"projectionMat",this.camera.projectionMatrix); this.program.projectionMat = this.camera.projectionMatrix;
this.texture.bind(gl,0); this.program.setUniform3fv(gl,"cameraEyePosition",this.camera.pos);
this.program.setUniform1i(gl,"mainTexture",0); const colorTextureID = 0;
this.texture.bind(gl,colorTextureID);
this.program.colorTextureID = colorTextureID;
if(this.teapot !== undefined){ 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]]){ 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); this.teapot.draw(gl);
} }
} }
if(this.cube !== undefined){ if(this.cube !== undefined){
this.program.updateMaterial({
shininess:20,
specularIntensity:0.5
});
const modelMat = mat4.create(); const modelMat = mat4.create();
mat4.identity(modelMat); mat4.identity(modelMat);
mat4.translate(modelMat,modelMat,[45,0,0]); mat4.translate(modelMat,modelMat,[45,0,0]);
mat4.scale(modelMat,modelMat,[5,5,5]); mat4.scale(modelMat,modelMat,[5,5,5]);
//mat4.rotateX(modelMat,modelMat,Math.PI * 30 / 180); //mat4.rotateX(modelMat,modelMat,Math.PI * 30 / 180);
this.program.setUniformMat4f(gl,"modelMat",modelMat); this.program.modelMat = modelMat;
this.cube.draw(gl); 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);
}
} }

View File

@ -1,18 +1,46 @@
#version 300 es #version 300 es
precision highp float; 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 vec3 fragNormal;
in vec2 texUV; 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() { void main() {
vec3 lightPos = vec3(20,20,1); vec3 lightAmbient = light.color * light.ambientIntensity;
vec3 c = normalize(lightPos); vec3 lightDiffuse = calcDiffuse();
float intense = dot(c,normalize(fragNormal)); vec3 lightSpecular = calcSpecular();
intense = (max(intense,-0.5)+1.0)/2.0; vec4 materialColor = texture(material.colorTexture,texUV);
vec4 objectColor = texture(mainTexture,texUV); materialColor *= vec4(lightAmbient + lightDiffuse + lightSpecular,1.0);
outColor = vec4(objectColor.xyz * intense,1); outColor = vec4(materialColor.xyz,1.0);
} }

View File

@ -1,19 +1,32 @@
import * as React from 'react'; import * as React from 'react';
import Slider from "@mui/material/Slider"; import Slider from "@mui/material/Slider";
import Typograpy from "@mui/material/Typography"; 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}){ export function MenuBar(prop:{u:UniformSet,onUniformChange:(u:UniformSet)=>void}){
const [uniform,setUniform] = React.useState(prop.u); const [uniform,setUniform] = React.useState(prop.u);
const onRedChange = (_,v)=>{ const onDiffIntenseChange = (_,v)=>{
setUniform({...uniform,redcolor:v/100}); setUniform({...uniform,light_diffuseIntensity:v/100});
};
const onAmbientIntenseChange = (_,v)=>{
setUniform({...uniform,light_ambientIntensity:v/100});
}; };
React.useEffect(()=>{ React.useEffect(()=>{
prop.onUniformChange(uniform); prop.onUniformChange(uniform);
}); });
const a = uniform.redcolor*100; const diffuseIntense = uniform.light_diffuseIntensity*100;
const ambientIntense = uniform.light_ambientIntensity*100;
return (<div> return (<div>
<Typograpy variant="h4">Uniform</Typograpy> <Typograpy variant="h4">Light Diffuse Intense</Typograpy>
<Slider aria-label="Small" valueLabelDisplay="auto" onChange={onRedChange} value={Math.round(a)}></Slider> <Slider aria-label="Small" valueLabelDisplay="auto" onChange={onDiffIntenseChange} value={Math.round(diffuseIntense)}></Slider>
<Typograpy variant="h4">Light Ambient Intense</Typograpy>
<Slider aria-label="Small" valueLabelDisplay="auto" onChange={onAmbientIntenseChange} value={Math.round(ambientIntense)}></Slider>
</div>); </div>);
} }

88
src/phongProgram.ts Normal file
View File

@ -0,0 +1,88 @@
/// <reference path="./glsl.d.ts" />
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);
}
}

View File

@ -1,9 +1,11 @@
export type UniformSet = { import { DirectionalLight } from "./phongProgram";
redcolor:number import { UniformSet } from "./menu";
};
export function getUniformDefaultValue():UniformSet{ export function dirLightFromMenuParam(u: UniformSet): DirectionalLight{
return { return {
redcolor:0.5, ambientIntensity: u.light_ambientIntensity,
} diffuseIntensity: u.light_diffuseIntensity,
color: u.light_color,
direction: u.light_direction
};
} }

View File

@ -1,5 +1,6 @@
#version 300 es #version 300 es
layout(location=0) in vec3 position; layout(location=0) in vec3 position;
layout(location=1) in vec2 textureUV; layout(location=1) in vec2 textureUV;
layout(location=2) in vec3 normal; layout(location=2) in vec3 normal;
@ -10,10 +11,11 @@ uniform mat4 projectionMat;
out vec3 fragNormal; out vec3 fragNormal;
out vec2 texUV; out vec2 texUV;
out vec3 vertexWorldPosition;
void main() { void main() {
gl_Position = projectionMat * viewMat * modelMat * vec4(position,1); gl_Position = projectionMat * viewMat * modelMat * vec4(position,1);
fragNormal = mat3(transpose(inverse(modelMat))) * normal; fragNormal = mat3(transpose(inverse(modelMat))) * normal;
vertexWorldPosition = (modelMat * vec4(position,1.0)).xyz;
texUV = textureUV; texUV = textureUV;
} }