add 3d drawer

This commit is contained in:
monoid 2021-10-10 02:07:34 +09:00
parent 5092540715
commit 1e6314fcdd
7 changed files with 212 additions and 18 deletions

View File

@ -17,6 +17,7 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@parcel/transformer-glsl": "^2.0.0-rc.0",
"@types/react": "^17.0.27", "@types/react": "^17.0.27",
"parcel": "^2.0.0-rc.0", "parcel": "^2.0.0-rc.0",
"webgl-strict-types": "^1.0.5" "webgl-strict-types": "^1.0.5"

View File

@ -1,18 +1,53 @@
import { Drawer2D } from "./drawer2D"; import { Drawer2D } from "./drawer2D";
import { Drawer3D } from "./drawer3D";
import { TriangleDrawer } from "./triangle_drawer"; import { TriangleDrawer } from "./triangle_drawer";
export class CanvasApp{ export class CanvasApp{
readonly gl: WebGL2RenderingContext; readonly gl: WebGL2RenderingContext;
renderer: Drawer2D; renderer: Drawer2D;
trenderer : TriangleDrawer; trenderer : TriangleDrawer;
r : Drawer3D;
constructor(gl: WebGL2RenderingContext){ constructor(gl: WebGL2RenderingContext){
this.gl = gl; this.gl = gl;
this.renderer = new Drawer2D(gl); this.renderer = new Drawer2D(gl);
this.trenderer = new TriangleDrawer(gl); this.trenderer = new TriangleDrawer(gl);
this.r = new Drawer3D(gl);
} }
intialize():boolean{ intialize():boolean{
this.renderer.prepare(); this.renderer.prepare();
this.trenderer.prepare(this.gl); this.trenderer.prepare(this.gl);
this.r.init();
document.addEventListener("keydown",(e)=>{
if(e.key == "h"){
this.r.camera.rotateRight(Math.PI * 5 / 180.0);
}
if(e.key == "f"){
this.r.camera.rotateRight(-Math.PI * 5 / 180.0)
}
if(e.key == "t"){
this.r.camera.rotateUp(Math.PI * 5 / 180.0)
}
if(e.key == "g"){
this.r.camera.rotateUp(-Math.PI * 5 / 180.0)
}
if(e.key == "d"){
this.r.camera.pos[0] += 0.5;
}
if(e.key == "a"){
this.r.camera.pos[0] -= 0.5;
}
if(e.key == "w"){
this.r.camera.pos[2] += 0.5;
}
if(e.key == "s"){
this.r.camera.pos[2] -= 0.5;
}
});
this.gl.clearColor(0,0,0,0);
this.gl.clearDepth(1);
this.gl.enable(this.gl.DEPTH_TEST);
return true; return true;
} }
startRun(){ startRun(){
@ -32,11 +67,10 @@ export class CanvasApp{
} }
drawScene(){ drawScene(){
this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height); this.gl.viewport(0,0,this.gl.canvas.width,this.gl.canvas.height);
this.gl.clear(this.gl.COLOR_BUFFER_BIT|this.gl.DEPTH_BUFFER_BIT);
this.gl.clearColor(0,0,0,0); //this.renderer.draw(this.gl,{});
this.gl.clear(this.gl.COLOR_BUFFER_BIT); //this.trenderer.draw(this.gl,{});
this.renderer.draw(this.gl,{}); this.r.draw(this.gl,{});
this.trenderer.draw(this.gl,{});
requestAnimationFrame(this.drawScene.bind(this)); requestAnimationFrame(this.drawScene.bind(this));
} }
}; };

View File

@ -3,12 +3,20 @@ import {mat4, vec3, quat, mat3} from "gl-matrix";
export class Camera{ export class Camera{
pos:vec3; pos:vec3;
rot:quat; rot:quat;
#proj : mat4;
fovY : number;
aspect : number;
far : number;
near: number;
constructor(pos:vec3,rot?:quat){ constructor(pos:vec3,rot?:quat){
this.pos = pos; this.pos = pos;
this.#proj = mat4.create();
if(rot == undefined){ if(rot == undefined){
const lookAt = mat4.lookAt(mat4.create(),this.pos,[0,0,0],[0,0,1]) const lookAt = mat4.lookAt(mat4.create(),this.pos,[0,0,0],[0,1,0]);
const rotMat = mat3.fromMat4(mat3.create(),lookAt); const rotMat = mat3.fromMat4(mat3.create(),lookAt);
this.rot = quat.fromMat3(quat.create(), rotMat); this.rot = quat.fromMat3(quat.create(), rotMat);
//quat.normalize(this.rot,this.rot);
//quat.conjugate(this.rot,this.rot);
} }
else{ else{
this.rot = rot; this.rot = rot;
@ -18,14 +26,24 @@ export class Camera{
quat.rotateY(this.rot,this.rot,rad); quat.rotateY(this.rot,this.rot,rad);
} }
rotateUp(rad:number){ rotateUp(rad:number){
quat.rotateX(this.rot,this.rot,rad);
}
rotateClockwise(rad:number){
quat.rotateZ(this.rot,this.rot,rad); quat.rotateZ(this.rot,this.rot,rad);
} }
getViewMat(){ rotateClockwise(rad:number){
quat.rotateX(this.rot,this.rot,rad);
}
get viewMatrix():mat4{
const p = vec3.negate(vec3.create() ,this.pos);
const t = mat4.fromTranslation(mat4.create(),p);
const r = mat4.fromQuat(mat4.create(),this.rot); const r = mat4.fromQuat(mat4.create(),this.rot);
mat4.translate(r,r,this.pos); return mat4.mul(mat4.create(),r,t);
return r; }
/**
* update projection matrix
*/
UpdateProjectionMat(){
mat4.perspective(this.#proj,this.fovY,this.aspect,this.near,this.far);
}
get projectionMatrix():mat4{
return this.#proj;
} }
}; };

60
src/drawer3D.ts Normal file
View File

@ -0,0 +1,60 @@
/// <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 } from "gl-matrix";
export class Drawer3D implements Drawable{
gl : WebGL2RenderingContext;
program: G.GLProgram;
camera : Camera;
model: Model | undefined;
constructor(gl: WebGL2RenderingContext){
this.gl = gl;
this.camera = new Camera([-20,1,-10]);
this.camera.far = 1000;
this.camera.near = 0.1;
this.camera.fovY = Math.PI * 90 / 180;
this.camera.aspect = 1;
this.model = undefined;
try{
this.program = new G.GLProgram(createProgramFromSource(gl,vert_src,frag_src));
const attr = this.program.getActiveAttributes(gl);
console.log(attr);
}
catch(e){
if(e instanceof ShaderError){
console.log(e.info);
}
throw e;
}
}
async init(){
const gl = this.gl;
const url = new URL("../assets/models/teapot/teapot.obj",import.meta.url);
this.model = await Model.loadFromOBJ(gl,url.href);
console.log("loading model complete");
this.model.ready(gl,this.program);
}
draw(gl: WebGL2RenderingContext,state:RenderState): void {
if(this.model !== undefined){
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.program.setUniformMat4f(gl,"modelMat",mat4.fromTranslation(mat4.create(),[0,0,0]));
this.model.draw(gl);
}
}
}

View File

@ -1,10 +1,11 @@
#version 300 es #version 300 es
precision highp float; precision highp float;
layout(location=0) out vec4 outColor; layout(location=0) out vec4 outColor;
//in vec4 gl_FragCoord; in vec3 fragNormal;
//in vec2 gl_PointCoord;
uniform vec4 u_color;
void main() { void main() {
outColor = u_color; vec3 c = normalize(vec3(20,20,1));
float intense = dot(c,normalize(fragNormal));
intense = (max(intense,-0.5)+1.0)/2.0;
outColor = vec4(vec3(1,1,0) * intense,1);
} }

70
src/model.ts Normal file
View File

@ -0,0 +1,70 @@
import { createIndexBuffer, createVertexArray, createVertexBuffer, GLProgram, IndexBuffer, VertexArray, VertexBuffer } from "./glWrapper";
import {OBJ} from "webgl-obj-loader";
import { assertBoolean } from "./util";
type VertexType = "position" | "normal" | "textureUV";
type Vertexes = {[key in VertexType]:VertexBuffer};
export class Model{
vertexes : Vertexes;
ibo :IndexBuffer;
vao :VertexArray;
constructor(gl:WebGL2RenderingContext,vertex:Vertexes,index :IndexBuffer){
this.vertexes = vertex;
this.vao = createVertexArray(gl);
this.ibo = index;
}
ready(gl:WebGL2RenderingContext,p : GLProgram){
const VertexLayouts = {
"position":{
count:3,
type:gl.FLOAT,
normalize:false,
stride:0,
offset:0
},
"normal":{
count:3,
type:gl.FLOAT,
normalize:false,
stride:0,
offset:0
},
"textureUV":{
count:2,
type:gl.FLOAT,
normalize:false,
stride:0,
offset:0
}
}
this.vao.bind(gl);
for(const [name,buf] of Object.entries(this.vertexes)){
const loc = p.getAttribLocation(gl,name);
assertBoolean(loc >= 0,"there is no",name,"attribute");
this.vao.addBuffer(gl,buf,loc,VertexLayouts[name]);
}
this.ibo.bind(gl);
this.vao.unbind(gl);
}
draw(gl:WebGL2RenderingContext){
this.vao.bind(gl);
gl.drawElements(gl.TRIANGLES,this.ibo.count,gl.UNSIGNED_SHORT,0);
this.vao.unbind(gl);
}
static async loadFromOBJ(gl:WebGL2RenderingContext,src:RequestInfo,init?:RequestInit){
const response = await fetch(src,init);
const text = await response.text();
const mesh = new OBJ.Mesh(text);
const position = createVertexBuffer(gl,mesh.vertices);
const normal = createVertexBuffer(gl,mesh.vertexNormals);
const texture = createVertexBuffer(gl,mesh.textures);
const index = createIndexBuffer(gl,mesh.indices);
return new Model(gl,{
position: position,
normal: normal,
textureUV: texture
}, index);
}
}

View File

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