【WebGL】纯手写Web端渲染器及其API
效果展示:https://1keven1.github.io/BlogSrc/WebGLRenderer/SimpleDemo.html
资源网址:https://github.com/1keven1/WebGLRenderer
高频更新中,内容可能滞后或有变动
效果展示:
123简介
在认真阅读并学习了WebGL Programming Guide一书后,萌生了制作一个Web端渲染器的想法。由于Web端的全平台特性,之后做了什么材质特效,可以在这个平台上制作展示效果后,无论是手机还是什么设备都可以实时观看,同时也可以很方便地以iframe的形式插入到博文中,顺便还能找机会学学web三件套,可谓是一举多得。
虽然我基本是完全没用过web三件套,但是我不信我硬写写不出来,直接开干。
结果,嘿,您猜怎么着?还真给我写出来了!
界面以及操作
预期要实现的功能为:可以编写简单脚本;可以修改shader并看到效果;可以旋转摄像机,放大缩小;
- 在示例项目中,使用了SimpleRotateCamera作为摄像机,所以在视口左键可以旋转视角,右键可以缩放;
- 点击右下角标签可以显示我自己实现的代码编辑器,在其中有指定想要编辑的脚本和shader,编辑后点击右下角应用即可;
- 代码编辑器中第一个Script是场景脚本,可以随意修改场景相关逻辑,其他的则是指定的editableShaderList中的shader;
设计框架介绍
- 整体为一个WebGLRenderer类,其中有表示场景的Scene类和负责代码编辑器的CodeEditor类;
- Scene类中储存着渲染需要用到的各种资源,渲染的基本单位为Mesh类;
- Actor类为可放置在世界中物品的基类,从其中派生出了Mesh、Light、Camera类。其中Mesh为可渲染的物品,包含一个模型(Model类)和一个材质(Material类);Light为灯光;Camera为摄像机;
- Model类是3D模型,通过读入的Obj文件加载(现在只支持三角面,且面数小于2w);
- Material类是材质类,包含三个着色器(Shader类):Base、Add、ShadowCaster,分别负责主要光源着色、其他光源着色、阴影投射。玩家赋予的材质参数及其值也会储存在其中;
- Shader类是着色器类,包括顶点着色器和片段着色器,通过读入的GLSL文件编译生成着色器代码。
制作教程
- 【WebGL】从零开始的Web端渲染器(1)——框架搭建
- 【WebGL】从零开始的Web端渲染器(2)——添加阴影
- 【WebGL】从零开始的Web端渲染器(3)——法线贴图
- 【WebGL】从零开始的Web端渲染器(4)——贴图及阴影抗锯齿(还在写)
使用教程
可编辑Script
控制整个场景的生成以及运行逻辑的脚本,在确认修改后会刷新渲染器重新开始渲染。整体而言还算比较复杂,所以基本上只用修改,不用删掉重写。
脚本结构:
1 | // 新建场景内各类元素 |
各种类的构造函数请看API。
可用资源
资源更新频繁
具体请看https://github.com/1keven1/BlogSrc/tree/main/WebGLRenderer/Res
以及https://github.com/1keven1/BlogSrc/tree/main/WebGLRenderer/DefaultShader
图片资源(/Res/Image/)
有一些简单的测试用图片;
- Test.jpg:魂三洋葱哥Q版,1k;
- Sky.jpg:WebGL Programming Guide使用的天空贴图,256*256;
- Drop.png:简单的蒙版贴图,512*512;
材质贴图资源(/Res/Material/)
各种PBR材质贴图(虽然我这还没实现PBR渲染罢),BC (Base Color) N (Normal) R(Roughness) M (Metallic) AO (Ambient Occlusion);
- Floor_BC、Floor_N、Floor_R、Floor_AO:Quixel Megascans下的Stone_Floor贴图资源,1k;
- Pavement_BC、Pavement_N、Pavement_R:步行街道资源,1k;
3D模型资源(/Res/Model/)
常用以及测试用3D模型;
- Cube.obj、Plane.obj、Quad.obj、Sphere.obj:最基础的简单模型;
- Bunny5k.obj:斯坦福兔模型,5k面;
- Suzanne.obj:猴头模型;
默认Shader(/DefaultShader/)
- DefaultVert.vert:常用的顶点着色器,传出v_TexCoord、v_WorldNormal、v_WorldTangent、v_WorldBinormal、v_viewDir和v_PositionFromLight(用于计算阴影),正常使用基本够用。
- Diffuse.frag、DiffuseBC.frag、DiffuseBCN.frag:简单的漫反射材质,bc为带基础色贴图,n为法线贴图;
- Specular.frag、SpecularBC.frag、SpecularBCN.frag:简单的高光反射材质,bc为带基础色贴图,n为法线贴图;
- ShadowCaster.vert、ShadowCaster.frag:阴影投射shader,一般不用改,除非是顶点动画。
- S_BCNRAO.frag:用于高品质PBR材质的渲染,需要BC、N、R、AO贴图(金属度还没实现)
API
Scene
Scene(modelList, materialList, textureList, meshList, lightList, camera)
整个场景,只有输入的资源会被加载
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
modelList | Model[] | 需要加载的模型 | null |
materialList | Material[] | 需要加载的材质 | null |
textureList | Texture[] | 需要加载的贴图 | null |
meshList | Mesh[] | 场景内的Mesh | null |
lightList | Light[] | 场景类的光源 | null |
camera | Camera | 渲染所用相机 | null |
Actor
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
Actor(transform)
一个具有Transform属性的,可以被放置在世界中的物体,是所有可以被放置在世界中的物体的基类。
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
transform | Transform | 变换 | new Transform() |
getLocation() -> Vector3
返回位移信息
返回:位移的拷贝
getRotation() -> Vector3
返回旋转信息
返回:旋转的拷贝
getScale() -> Vector3
返回缩放信息
返回:缩放的拷贝
setLocation(location)
设置位置
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
location | Vector3 | 位置信息 | null |
setRotation(rotation)
设置旋转值
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
rotation | Vector3 | 旋转信息 | null |
setLocationAndRotation(location, rotation)
设置位置和旋转值
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
location | Vector3 | 位置信息 | null |
rotation | Vector3 | 旋转信息 | null |
setScale(scale)
设置缩放值
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
scale | Vector3 | 缩放信息 | null |
addLocationOffset(offset)
添加位移偏移
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
offset | Vector3 | 位移偏移 | null |
addRotationOffset(offset)
添加旋转偏移
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
offset | Vector3 | 旋转偏移 | null |
getRotationMatrix() -> Matrix4
获取旋转矩阵
返回值:旋转矩阵
getForwardVector() -> Vector3
获取向前向量
返回值:向前向量
getUpVector() -> Vector3
获取向上向量
返回值:向上向量
getRightVector() -> Vector3
获取向右向量
返回值:向右向量
distanceToActor(actor) -> Number
返回与另一个Actor的距离
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
actor | Actor | 另一个Actir | null |
返回值:距离
Mesh(继承自Actor)
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
Mesh(transform, model, material, bCastShadow)
可以被渲染的Actor 有一个Model和一个Material
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
transform | Transform | 变换 | null |
model | Model | 模型 | null |
material | Material | 材质 | null |
bCastShadow | boolean | 投射阴影 | true |
setCastShadow(bCastShadow)
设置是否投射阴影
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
bCastShadow | boolean | 投射阴影 | null |
copy() -> Mesh
复制一个相同的Mesh出来
返回值:复制的Mesh
Light(继承自Actor)
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
Light(transform, lightColor, intensity, lightType)
照亮场景的灯光,种类为LIGHT_TYPE,目前只实现了定向光。默认shadow map分辨率为2k。
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
transform | Transform | 变换 | null |
lightColor | Vector3 | 光照颜色 | new Vector3([1, 1, 1]) |
intensity | Number | 强度 | 1 |
lightType | LIGHT_TYPE | 光源类型 | LIGHT_TYPE.DIRECTIONAL |
Camera(继承自Actor)
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
Camera(transform, FOV, nearClip, farClip)
最简单的渲染世界的摄像机
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
transform | Transform | 变换 | null |
FOV | Number | 视野角度 | 60 |
nearClip | Number | 近剪裁平面 | 0.1 |
farClip | Number | 远剪裁平面 | 100 |
SimpleRotateCamera(继承自Camera)
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
SimpleRotateCamera(lookAtPoint, distance, FOV, nearClip, farClip)
始终看向一点,并可以绕其旋转的相机
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
lookAtPoint | Vector3 | 看向的目标点 | new Vector3([0, 0, 0]) |
distance | Number | 距目标点的距离 | 6 (2 ~ 10) |
FOV | Number | 视野角度 | 60 |
nearClip | Number | 近剪裁平面 | 0.1 |
farClip | Number | 远剪裁平面 | 100 |
Shader
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Shader.js
Shader(vShaderFile, fShaderFile)
着色器,由顶点着色器和片元着色器文件组成
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
vShaderFile | String | 顶点着色器路径 | null |
fShaderFile | String | 片元着色器路径 | null |
Material
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Material.js
Material(baseShader, shadowCaster, materialType, queueOffset)
材质,需要一个Base着色器与阴影投射着色器,种类有MATERIAL_TYPE
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
baseShader | Shader | Base着色器 用于计算主光源 | null |
shadowCaster | Shader | 阴影投射着色器 用于投射阴影 | null |
materialType | MATERIAL_TYPE | 材质类型 | MATERIAL_TYPE.OPAQUE |
queueOffset | Number | 渲染队列偏移 | 0 |
setMaterialType(materialType, offset)
设置材质类型
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
materialType | MATERIAL_TYPE | 材质类型 | null |
offset | Number | 渲染队列偏移 | null |
setBlendFactor(srcFactor, desFactor)
设置混合系数
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
srcFactor | GLenum | 源混合系数 | null |
desFactor | GLenum | 目标混合系数 | null |
setCullMode(cullMode)
设置材质剔除模式系数
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
cullMode | CULL_MODE | 剔除模式 | null |
setVector3f(param, x, y, z)
设置Shader三维参数
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
param | String | 参数名称 | null |
x | Number | x | 0 |
y | Number | y | 0 |
z | Number | z | 0 |
setVector4f(param, x, y, z, w)
与上面的类似
setTexture(param, texture)
与上面的类似
Model
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Model.js
Model(objFile, scale)
模型为Obj格式3D模型文件,目前只支持三角面
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
objFile | String | Obj文件路径 | null |
scale | Number | 模型缩放 | 1 |
Texture
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Texture.js
Texture(texFile, texType)
贴图文件,会自动绑定到Texture Unit
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
texFile | String | 贴图文件路径 | null |
texUnit | GLenum | 贴图类型 | gl.TEXTURE_2D |
Transform
代码:https://github.com/1keven1/WebGLRenderer/blob/main/Actor.js
Transform(location, rotation, scale)
储存变换信息的结构体
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
lodation | Vector3 | 位移 | new Vector3([0, 0, 0]) |
rotation | Vector3 | 旋转 | new Vector3([0, 0, 0]) |
scale | Vector3 | 缩放 | new Vector3([0, 0, 0]) |
copy() -> Transform
复制一份Transform出来
返回值:复制出的Transform
Vector3
更新中…
可以看这里:https://github.com/1keven1/WebGLRenderer/blob/main/Lib/cuon-matrix.js
和这里:https://github.com/1keven1/WebGLRenderer/blob/main/FunctionLibrary.js
枚举值
LIGHT_TYPE
光源种类
- DIRECTIONAL:定向光照
- POINT:点光源
- SPOT:聚光灯
MATERIAL_TYPE
材质类型(影响混合模式、深度检测和渲染队列)
- OPAQUE:不透明
- MASKED:遮罩
- TRANSLUCENT:半透明
- ADDITIVE:叠加
CULL_MODE
剔除模式
- BACK:剔除背面
- FRONT:剔除正面
- OFF:关闭剔除
其他函数库
代码:https://github.com/1keven1/WebGLRenderer/blob/main/FunctionLibrary.js
clamp(float, min, max) => Number
将输入值钳制在上下限之间
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
float | Number | 输入数据 | null |
min | Number | 下限 | 0 |
max | Number | 上限 | 1 |
返回值:钳制后的值
fInterpTo(current, target, deltaSecond, interpSpeed) => Number
对浮点数向目标值进行平滑差值,距离越近速度越慢
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
current | Number | 当前值 | null |
target | Number | 目标值 | null |
deltaSecond | Number | 世界Delta Time | null |
interpSpeed | Number | 插值速度 | null |
返回值:下一帧的值
isPowerOf2(number) => Boolean
判断某个数是否是二的整数次幂
输入名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
number | Number | 要检测的数 | null |
返回值:是否是二的次幂