feat: 地图渲染后处理

This commit is contained in:
unanmed 2025-11-26 15:12:30 +08:00
parent 42a7e15ed8
commit 3372337fdf
3 changed files with 159 additions and 12 deletions

View File

@ -15,10 +15,9 @@ export class MapRender extends RenderItem {
) { ) {
super('static', false, false); super('static', false, false);
this.renderer.setLayerState(layerState); renderer.setLayerState(layerState);
this.renderer.setCanvasSize(this.width, this.height); renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT);
this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT); renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT);
this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT);
this.delegateTicker(time => { this.delegateTicker(time => {
this.renderer.tick(time); this.renderer.tick(time);

View File

@ -188,6 +188,15 @@ export class MapRenderer
/** 图块动画器 */ /** 图块动画器 */
private readonly tileAnimater: ITextureAnimater<number>; private readonly tileAnimater: ITextureAnimater<number>;
/** `gl.viewport` 横坐标 */
private viewportX: number = 0;
/** `gl.viewport` 纵坐标 */
private viewportY: number = 0;
/** `gl.viewport` 宽度 */
private viewportWidth: number = 0;
/** `gl.viewport` 高度 */
private viewportHeight: number = 0;
//#endregion //#endregion
//#region 初始化 //#region 初始化
@ -227,6 +236,7 @@ export class MapRenderer
this.viewport = new MapViewport(this); this.viewport = new MapViewport(this);
this.tileAnimater = new TextureColumnAnimater(); this.tileAnimater = new TextureColumnAnimater();
this.initVertexPointer(this.gl, data); this.initVertexPointer(this.gl, data);
this.setViewport(0, 0, this.canvas.width, this.canvas.height);
} }
/** /**
@ -348,11 +358,43 @@ export class MapRenderer
setCanvasSize(width: number, height: number): void { setCanvasSize(width: number, height: number): void {
this.canvas.width = width; this.canvas.width = width;
this.canvas.height = height; this.canvas.height = height;
// 更新 FBO 的纹理尺寸信息
const gl = this.gl;
const { pingTexture2D, pongTexture2D } = this.contextData;
gl.bindTexture(gl.TEXTURE_2D, pingTexture2D);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
width,
height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.bindTexture(gl.TEXTURE_2D, pongTexture2D);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
width,
height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.bindTexture(gl.TEXTURE_2D, null);
this.updateRequired = true; this.updateRequired = true;
} }
setViewport(x: number, y: number, width: number, height: number): void { setViewport(x: number, y: number, width: number, height: number): void {
this.gl.viewport(x, y, width, height); this.viewportX = x;
this.viewportY = y;
this.viewportWidth = width;
this.viewportHeight = height;
this.updateRequired = true;
} }
clear(color: boolean, depth: boolean): void { clear(color: boolean, depth: boolean): void {
@ -674,6 +716,57 @@ export class MapRenderer
const tileVAO = gl.createVertexArray(); const tileVAO = gl.createVertexArray();
const backVAO = gl.createVertexArray(); const backVAO = gl.createVertexArray();
// Post effect
const pingFramebuffer = gl.createFramebuffer();
const pongFramebuffer = gl.createFramebuffer();
const pingTexture2D = gl.createTexture();
const pongTexture2D = gl.createTexture();
// 初始化 Post effect FBO 和 Texture
gl.bindTexture(gl.TEXTURE_2D, pongTexture2D);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
this.canvas.width,
this.canvas.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.bindFramebuffer(gl.FRAMEBUFFER, pingFramebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
pongTexture2D,
0
);
gl.bindTexture(gl.TEXTURE_2D, pingTexture2D);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
this.canvas.width,
this.canvas.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.bindFramebuffer(gl.FRAMEBUFFER, pongFramebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
pingTexture2D,
0
);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, null);
// 初始化图块纹理
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tileTexture); gl.bindTexture(gl.TEXTURE_2D_ARRAY, tileTexture);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, 4096, 4096, 1); gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, 4096, 4096, 1);
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null); gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
@ -717,6 +810,12 @@ export class MapRenderer
backVAO, backVAO,
tileTexture, tileTexture,
backgroundTexture, backgroundTexture,
pingFramebuffer,
pongFramebuffer,
pingTexture2D,
pongTexture2D,
tileTextureWidth: 4096, tileTextureWidth: 4096,
tileTextureHeight: 4096, tileTextureHeight: 4096,
tileTextureDepth: 1, tileTextureDepth: 1,
@ -1273,7 +1372,11 @@ export class MapRenderer
insTilePosAttribLocation: tilePos, insTilePosAttribLocation: tilePos,
insTexCoordAttribLocation: texCoord, insTexCoordAttribLocation: texCoord,
insTileDataAttribLocation: tileData, insTileDataAttribLocation: tileData,
insTexDataAttribLocation: texData insTexDataAttribLocation: texData,
pingFramebuffer,
pongFramebuffer,
pingTexture2D,
pongTexture2D
} = data; } = data;
// 图层检查 // 图层检查
@ -1311,6 +1414,19 @@ export class MapRenderer
gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindBuffer(gl.ARRAY_BUFFER, null);
} }
gl.viewport(
this.viewportX,
this.viewportY,
this.viewportWidth,
this.viewportHeight
);
if (this.postEffects.length > 1) {
gl.bindFramebuffer(gl.FRAMEBUFFER, pingFramebuffer);
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
// 背景 // 背景
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(backProgram); gl.useProgram(backProgram);
@ -1365,6 +1481,28 @@ export class MapRenderer
gl.bindVertexArray(null); gl.bindVertexArray(null);
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null); gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
// Post effects
let inputTextrue = pongTexture2D;
let outputFBO: WebGLFramebuffer | null = pingFramebuffer;
this.postEffects.forEach((v, i, a) => {
v.render(gl, inputTextrue, outputFBO, data);
if (inputTextrue === pongTexture2D) {
inputTextrue = pingTexture2D;
} else {
inputTextrue = pongTexture2D;
}
if (i === a.length - 2) {
outputFBO = null;
} else {
if (outputFBO === pingFramebuffer) {
outputFBO = pongFramebuffer;
} else {
outputFBO = pingFramebuffer;
}
}
});
// 清空更新状态标识 // 清空更新状态标识
this.updateRequired = false; this.updateRequired = false;
this.needUpdateFrameCounter = false; this.needUpdateFrameCounter = false;

View File

@ -131,6 +131,15 @@ export interface IContextData {
/** 背景程序的 VAO */ /** 背景程序的 VAO */
readonly backVAO: WebGLVertexArrayObject; readonly backVAO: WebGLVertexArrayObject;
/** 第一个 framebuffer */
readonly pingFramebuffer: WebGLFramebuffer;
/** 第二个 framebuffer */
readonly pongFramebuffer: WebGLFramebuffer;
/** 第一个 texture2D */
readonly pingTexture2D: WebGLTexture;
/** 第二个 texture2D */
readonly pongTexture2D: WebGLTexture;
/** 当前画布的图块纹理宽度 */ /** 当前画布的图块纹理宽度 */
tileTextureWidth: number; tileTextureWidth: number;
/** 当前画布的图块纹理高度 */ /** 当前画布的图块纹理高度 */
@ -280,17 +289,18 @@ export interface IMapRendererPostEffect {
init(gl: WebGL2RenderingContext, data: IContextData): void; init(gl: WebGL2RenderingContext, data: IContextData): void;
/** /**
* FBO * FBO 使 `gl.viewport`
* FBO
* @param gl WebGL2 * @param gl WebGL2
* @param input Texture2D
* @param output FBO FBO `null`
* @param data * @param data
* @param input FBO
* @param output FBO FBO
*/ */
render( render(
gl: WebGL2RenderingContext, gl: WebGL2RenderingContext,
data: IContextData, input: WebGLTexture,
input: WebGLFramebuffer, output: WebGLFramebuffer | null,
output: WebGLFramebuffer data: IContextData
): void; ): void;
} }