Implement classic renderer

This commit is contained in:
MrKBear 2022-02-16 16:28:13 +08:00
parent 3efb691601
commit e188fc83bd
7 changed files with 274 additions and 80 deletions

View File

@ -1,7 +1,7 @@
import { BasicsShader } from "./BasicShader";
import { DisplayObject } from "./DisplayObject";
class Axis extends DisplayObject{
class Axis extends DisplayObject<BasicsShader>{
/**
*
@ -24,6 +24,10 @@ class Axis extends DisplayObject{
this.gl.bufferData(this.gl.ARRAY_BUFFER, Axis.AXIS_VER_DATA, this.gl.STATIC_DRAW);
}
public clean(): void {
this.gl.deleteBuffer(this.axisVertexBuffer);
}
/**
*
*/
@ -34,39 +38,39 @@ class Axis extends DisplayObject{
/**
*
*/
public draw(shader: BasicsShader){
public draw(){
// 使用程序
shader.use();
this.shader.use();
// 绑定缓冲区
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.axisVertexBuffer);
// 指定指针数据
this.gl.vertexAttribPointer(
shader.attribLocate("aPosition"),
this.shader.attribLocate("aPosition"),
3, this.gl.FLOAT, false, 0, 0);
// mvp参数传递
shader.mvp(this.camera.transformMat);
this.shader.mvp(this.camera.transformMat);
// 半径传递
shader.radius([this.r, this.r, this.r]);
shader.position(this.pos);
this.shader.radius([this.r, this.r, this.r]);
this.shader.position(this.pos);
shader.fogColor(this.renderer.fogColor);
shader.fogDensity(this.renderer.fogDensity);
this.shader.fogColor(this.renderer.fogColor);
this.shader.fogDensity(this.renderer.fogDensity);
// 绘制 X 轴
shader.color([1, 0, 0]);
this.shader.color([1, 0, 0]);
this.gl.drawArrays(this.gl.LINES, 0, 2);
// 绘制 Y 轴
shader.color([0, 1, 0]);
this.shader.color([0, 1, 0]);
this.gl.drawArrays(this.gl.LINES, 2, 2);
// 绘制 Z 轴
shader.color([0, 0, 1]);
this.shader.color([0, 0, 1]);
this.gl.drawArrays(this.gl.LINES, 4, 2);
}
}

View File

@ -1,7 +1,7 @@
import { DisplayObject } from "./DisplayObject";
import { BasicsShader } from "./BasicShader";
class BaseCube extends DisplayObject{
class BasicCube extends DisplayObject<BasicsShader>{
/**
*
@ -28,10 +28,15 @@ class BaseCube extends DisplayObject{
// 绑定缓冲区
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexBuffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, BaseCube.CUBE_VER_DATA, this.gl.STATIC_DRAW);
this.gl.bufferData(this.gl.ARRAY_BUFFER, BasicCube.CUBE_VER_DATA, this.gl.STATIC_DRAW);
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.cubeElementBuffer);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, BaseCube.CUBE_ELE_DATA, this.gl.STATIC_DRAW);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, BasicCube.CUBE_ELE_DATA, this.gl.STATIC_DRAW);
}
public clean(): void {
this.gl.deleteBuffer(this.cubeElementBuffer);
this.gl.deleteBuffer(this.cubeVertexBuffer);
}
private cubeVertexBuffer: WebGLBuffer | null = null;
@ -40,7 +45,7 @@ class BaseCube extends DisplayObject{
/**
*
*/
private r:[number,number,number] = [1, 1, 1];
public r:[number,number,number] = [1, 1, 1];
/**
*
@ -55,10 +60,10 @@ class BaseCube extends DisplayObject{
/**
*
*/
public draw(shader: BasicsShader){
public draw(){
// 使用程序
shader.use();
this.shader.use();
// 绑定缓冲区
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexBuffer);
@ -66,26 +71,32 @@ class BaseCube extends DisplayObject{
// 指定指针数据
this.gl.vertexAttribPointer(
shader.attribLocate("aPosition"),
this.shader.attribLocate("aPosition"),
3, this.gl.FLOAT, false, 0, 0);
// mvp参数传递
shader.mvp(this.camera.transformMat);
this.shader.mvp(this.camera.transformMat);
// 半径传递
shader.radius(this.r);
shader.position(this.position);
this.shader.radius(this.r);
this.shader.position(this.position);
// 指定颜色
shader.color(this.color);
this.shader.color(this.color);
shader.fogColor(this.renderer.fogColor);
shader.fogDensity(this.renderer.fogDensity);
this.shader.fogColor(this.renderer.fogColor);
this.shader.fogDensity(this.renderer.fogDensity);
// 开始绘制
this.gl.drawElements(this.gl.LINES, 24, this.gl.UNSIGNED_SHORT, 0);
}
public isCube: boolean = true;
public static isCube(object: DisplayObject): object is BasicCube {
return !!(object as BasicCube).isCube;
}
}
export default BaseCube;
export { BaseCube };
export default BasicCube;
export { BasicCube };

View File

@ -2,7 +2,7 @@ import { DisplayObject } from "./DisplayObject";
import { GroupShader } from "./GroupShader";
import { ObjectData } from "@Model/Renderer";
class BasicGroup extends DisplayObject{
class BasicGroup extends DisplayObject<GroupShader> {
private pointVertexBuffer: WebGLBuffer | null = null;
private pointVecMaxCount: number = 100 * 3;
@ -18,6 +18,10 @@ class BasicGroup extends DisplayObject{
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.pointVecMaxCount, this.gl.DYNAMIC_DRAW);
}
public clean(): void {
this.gl.deleteBuffer(this.pointVertexBuffer);
}
/**
* GPU
*/
@ -43,34 +47,40 @@ class BasicGroup extends DisplayObject{
/**
*
*/
public draw(shader: GroupShader){
public draw(){
// 使用程序
shader.use();
this.shader.use();
// 绑定缓冲区
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.pointVertexBuffer);
// 指定指针数据
this.gl.vertexAttribPointer(
shader.attribLocate("aPosition"),
this.shader.attribLocate("aPosition"),
3, this.gl.FLOAT, false, 0, 0);
// mvp参数传递
shader.mvp(this.camera.transformMat);
this.shader.mvp(this.camera.transformMat);
// 半径传递
shader.radius(this.size);
this.shader.radius(this.size);
// 指定颜色
shader.color(this.color);
this.shader.color(this.color);
shader.fogColor(this.renderer.fogColor);
shader.fogDensity(this.renderer.fogDensity);
this.shader.fogColor(this.renderer.fogColor);
this.shader.fogDensity(this.renderer.fogDensity);
// 开始绘制
this.gl.drawArrays(this.gl.POINTS, 0, this.pointVecCount);
}
public isGroup: boolean = true;
public static isGroup(object: DisplayObject): object is BasicGroup {
return !!(object as BasicGroup).isGroup;
}
}
export default BasicGroup;

View File

@ -2,11 +2,19 @@ import { ObjectID, ObjectData, ICommonParam } from "@Model/Renderer";
import { BasicRenderer } from "./BasicRenderer";
import { BasicsShader } from "./BasicShader";
import { Axis } from "./Axis";
import { BaseCube } from "./BasicCube";
import { BasicCube } from "./BasicCube";
import { GroupShader } from "./GroupShader";
import { BasicGroup } from "./BasicGroup";
import DisplayObject from "./DisplayObject";
interface IClassicRendererParams {}
interface IClassicRendererParams {
point: {
size: number;
}
cube: {
radius: number[];
}
}
class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
@ -22,14 +30,9 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
private readonly farFogLine = 2.5;
/**
*
*
*/
private groupPool = new Map<ObjectID, BasicGroup>();
/**
*
*/
private cubePool = new Map<ObjectID, BaseCube>();
private objectPool = new Map<ObjectID, DisplayObject>();
public onLoad(): void {
@ -38,19 +41,7 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
this.basicShader = new BasicsShader().bindRenderer(this);
this.groupShader = new GroupShader().bindRenderer(this);
this.axisObject = new Axis().bindRenderer(this);
// 测试渲染器
if (true) {
let cubeObject = new BaseCube().bindRenderer(this);
let basicGroup = new BasicGroup().bindRenderer(this);
// 生成随机数据测试
basicGroup.upLoadData(new Array(1000 * 3).fill(0).map(() => (Math.random() - .5) * 2));
this.cubePool.set("1", cubeObject);
this.groupPool.set("1", basicGroup);
}
this.axisObject = new Axis().bindRenderer(this).bindShader(this.basicShader);
this.canvas.on("mousemove", () => {
@ -99,16 +90,35 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
this.cleanCanvas();
// 绘制全部立方体
this.cubePool.forEach((cube) => {
if (cube.isDraw) cube.draw(this.basicShader);
else cube.drawEmptyFrame ++;
// 绘制全部物体
// 对象清理实现自动内存释放
let cleanArray: undefined | Set<ObjectID>;
this.objectPool.forEach((object, key) => {
if (object.drawEmptyFrame > this.maxDrawEmptyFrame) {
// 加入清除列表
if (!cleanArray) cleanArray = new Set<string>();
cleanArray.add(key);
// 释放显存
object.clean();
}
});
// 绘制全部点
this.groupPool.forEach((group) => {
if (group.isDraw) group.draw(this.groupShader);
else group.drawEmptyFrame ++;
if (cleanArray) {
cleanArray.forEach((key) => {
this.objectPool.delete(key);
console.log(`Renderer: Automatically clear objects ${key}`);
});
}
// 遍历绘制
this.objectPool.forEach((object) => {
if (object.isDraw) {
object.draw();
object.drawEmptyFrame = 0;
}
else object.drawEmptyFrame ++;
});
// 右上角绘制坐标轴
@ -119,19 +129,137 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
position * this.camera.ratio,
position
);
this.axisObject.draw(this.basicShader);
this.axisObject.draw();
}
/**
*
*/
private maxDrawEmptyFrame = 5000;
clean(id?: ObjectID | ObjectID[]): this {
throw new Error("Method not implemented.");
if (id) {
if (Array.isArray(id)) {
id.forEach((key) => {
let object = this.objectPool.get(key);
if (object) object.isDraw = false;
});
} else {
let object = this.objectPool.get(id);
if (object) object.isDraw = false;
}
} else {
this.objectPool.forEach((object) => {
object.isDraw = false;
});
}
return this;
}
points(id: ObjectID, position: ObjectData, param?: ICommonParam): this {
throw new Error("Method not implemented.");
points(
id: ObjectID, position?: ObjectData,
param?: Readonly<Partial<ICommonParam & IClassicRendererParams["point"]>>
): this {
let object = this.objectPool.get(id);
let group: BasicGroup;
if (object) {
if (BasicGroup.isGroup(object)) {
group = object;
// 数据上传
if (position) {
object.upLoadData(position);
}
} else {
throw new Error("Renderer: Use duplicate ObjectID when drawing different types of objects");
}
} else {
group = new BasicGroup().bindRenderer(this).bindShader(this.groupShader);
// 数据上传
group.upLoadData(position ?? []);
this.objectPool.set(id, group);
console.log(`Renderer: Create new group object with id ${id}`);
}
cube(id: ObjectID, position: ObjectData, param?: ICommonParam): this {
throw new Error("Method not implemented.");
// 开启绘制
group.isDraw = true;
// 参数传递
if (param) {
// 颜色数据
if (param.color) {
group.color[0] = param.color[0] ?? group.color[0]
group.color[1] = param.color[1] ?? group.color[1]
group.color[2] = param.color[2] ?? group.color[2]
}
// 半径数据
if (param.size) {
group.size = param.size;
}
}
return this;
}
cube(
id: ObjectID, position?: ObjectData,
param?: Readonly<Partial<ICommonParam & IClassicRendererParams["cube"]>>
): this {
let object = this.objectPool.get(id);
let cube: BasicCube;
if (object) {
if (BasicCube.isCube(object)) {
cube = object;
// 坐标数据上传
if (position) {
cube.position[0] = position[0] ?? cube.position[0];
cube.position[1] = position[1] ?? cube.position[1];
cube.position[2] = position[2] ?? cube.position[2];
}
} else {
throw new Error("Renderer: Use duplicate ObjectID when drawing different types of objects");
}
} else {
cube = new BasicCube().bindRenderer(this).bindShader(this.basicShader);
// 数据上传
if (position) {
cube.position[0] = position[0] ?? cube.position[0];
cube.position[1] = position[1] ?? cube.position[1];
cube.position[2] = position[2] ?? cube.position[2];
}
this.objectPool.set(id, cube);
console.log(`Renderer: Create new cube object with id ${id}`);
}
// 开启绘制
cube.isDraw = true;
// 参数传递
if (param) {
// 颜色数据
if (param.color) {
cube.color[0] = param.color[0] ?? cube.color[0]
cube.color[1] = param.color[1] ?? cube.color[1]
cube.color[2] = param.color[2] ?? cube.color[2]
}
// 半径数据
if (param.radius) {
cube.r[0] = param.radius[0] ?? cube.r[0];
cube.r[1] = param.radius[1] ?? cube.r[1];
cube.r[2] = param.radius[2] ?? cube.r[2];
}
}
return this;
}
}

View File

@ -1,7 +1,9 @@
import { EventType } from "@Model/Emitter";
import { GLContextObject } from "./GLContext";
import { GLShader } from "./GLShader";
abstract class DisplayObject<
S extends GLShader = GLShader,
E extends Record<EventType, any> = {}
> extends GLContextObject<E> {
@ -14,6 +16,30 @@ abstract class DisplayObject<
*
*/
public drawEmptyFrame: number = 0;
/**
* shader
*/
protected shader: S = undefined as any;
/**
*
*/
public bindShader(shader: S) {
this.shader = shader;
return this;
}
/**
*
*/
abstract draw(): void;
/**
*
*
*/
abstract clean(): void;
}
export { DisplayObject }

View File

@ -30,11 +30,6 @@ interface ICommonParam {
*
*/
color?: ObjectData;
/**
*
*/
radius?: number;
}
/**
@ -108,7 +103,7 @@ abstract class AbstractRenderer<
* @param id 使
* @param position
*/
abstract points(id: ObjectID, position: ObjectData, param?: P["points"] & ICommonParam): this;
abstract points(id: ObjectID, position?: ObjectData, param?: Readonly<P["points"] & ICommonParam>): this;
/**
* @function cube
@ -118,7 +113,7 @@ abstract class AbstractRenderer<
*
* 注意: 这里的半径指的是立方体重心与立方体任意一面几何中心的距离
*/
abstract cube(id: ObjectID, position: ObjectData, param?: P["cube"] & ICommonParam): this;
abstract cube(id: ObjectID, position?: ObjectData, param?: Readonly<P["cube"] & ICommonParam>): this;
}
export default AbstractRenderer;

View File

@ -28,7 +28,27 @@ class Laboratory extends Component {
renderer.onLoad();
console.log(renderer);
// 测试渲染器
if (true) {
renderer.points("0");
renderer.points("1", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2));
renderer.points("2", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), {
size: 100,
color: [1, 0, 1]
});
renderer.points("3", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), {
size: 80,
color: [0, 1, 1]
});
renderer.points("2");
renderer.cube("4");
renderer.cube("5", new Array(3).fill(0).map(() => (Math.random() - .5) * 2), {
radius: new Array(3).fill(0).map(() => Math.random() * 1.2),
color: [1, 1, 0]
})
}
(window as any).renderer = renderer;
this.canvasContRef.current.appendChild(renderer.canvas.dom);
}