mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +08:00
feat: Shader着色器组件
This commit is contained in:
parent
d7dcbe9d04
commit
2561d7f23d
@ -97,7 +97,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
|||||||
* 删除这个画布
|
* 删除这个画布
|
||||||
*/
|
*/
|
||||||
delete() {
|
delete() {
|
||||||
MotaCanvas2D.list.delete(this);
|
MotaOffscreenCanvas2D.list.delete(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
import { IRenderChildable, RenderItem, RenderItemPosition } from './item';
|
import {
|
||||||
|
ERenderItemEvent,
|
||||||
|
IRenderChildable,
|
||||||
|
RenderItem,
|
||||||
|
RenderItemPosition
|
||||||
|
} from './item';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
|
|
||||||
export class Container extends RenderItem implements IRenderChildable {
|
export interface EContainerEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Container<E extends EContainerEvent = EContainerEvent>
|
||||||
|
extends RenderItem<E | EContainerEvent>
|
||||||
|
implements IRenderChildable
|
||||||
|
{
|
||||||
children: RenderItem[] = [];
|
children: RenderItem[] = [];
|
||||||
sortedChildren: RenderItem[] = [];
|
sortedChildren: RenderItem[] = [];
|
||||||
|
|
||||||
@ -23,7 +33,6 @@ export class Container extends RenderItem implements IRenderChildable {
|
|||||||
transform: Transform
|
transform: Transform
|
||||||
): void {
|
): void {
|
||||||
const { ctx } = canvas;
|
const { ctx } = canvas;
|
||||||
// console.log(ctx.getTransform());
|
|
||||||
|
|
||||||
this.sortedChildren.forEach(v => {
|
this.sortedChildren.forEach(v => {
|
||||||
if (v.hidden) return;
|
if (v.hidden) return;
|
||||||
|
@ -155,7 +155,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
anchorY: number = 0;
|
anchorY: number = 0;
|
||||||
|
|
||||||
/** 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动 */
|
/** 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动 */
|
||||||
type: 'absolute' | 'static' = 'static';
|
type: RenderItemPosition = 'static';
|
||||||
/** 是否是高清画布 */
|
/** 是否是高清画布 */
|
||||||
highResolution: boolean = true;
|
highResolution: boolean = true;
|
||||||
/** 是否抗锯齿 */
|
/** 是否抗锯齿 */
|
||||||
@ -172,9 +172,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
transform: Transform = new Transform();
|
transform: Transform = new Transform();
|
||||||
|
|
||||||
/** 渲染缓存信息 */
|
/** 渲染缓存信息 */
|
||||||
private cache: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
protected cache: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||||
/** 是否需要更新缓存 */
|
/** 是否需要更新缓存 */
|
||||||
private cacheDirty: boolean = true;
|
protected cacheDirty: boolean = true;
|
||||||
/** 是否启用缓存机制 */
|
/** 是否启用缓存机制 */
|
||||||
readonly enableCache: boolean = true;
|
readonly enableCache: boolean = true;
|
||||||
|
|
||||||
@ -183,6 +183,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
|
|
||||||
this.enableCache = enableCache;
|
this.enableCache = enableCache;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
this.cache.withGameScale(true);
|
this.cache.withGameScale(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { FloorDamageExtends } from './preset/damage';
|
|||||||
import { HeroRenderer } from './preset/hero';
|
import { HeroRenderer } from './preset/hero';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
import { Text } from './preset/misc';
|
import { Text } from './preset/misc';
|
||||||
|
import { Shader } from './shader';
|
||||||
|
|
||||||
export class MotaRenderer extends Container {
|
export class MotaRenderer extends Container {
|
||||||
static list: Set<MotaRenderer> = new Set();
|
static list: Set<MotaRenderer> = new Set();
|
||||||
@ -74,6 +75,7 @@ Mota.require('var', 'hook').once('reset', () => {
|
|||||||
const transform = render.transform;
|
const transform = render.transform;
|
||||||
render.mount();
|
render.mount();
|
||||||
|
|
||||||
|
const shader = new Shader();
|
||||||
const layer = new LayerGroup();
|
const layer = new LayerGroup();
|
||||||
|
|
||||||
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
|
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
|
||||||
@ -87,7 +89,18 @@ Mota.require('var', 'hook').once('reset', () => {
|
|||||||
layer.extends(damage);
|
layer.extends(damage);
|
||||||
layer.getLayer('event')?.extends(hero);
|
layer.getLayer('event')?.extends(hero);
|
||||||
binder.bindThis();
|
binder.bindThis();
|
||||||
render.appendChild(layer);
|
render.appendChild(shader);
|
||||||
|
shader.appendChild(layer);
|
||||||
|
shader.size(480, 480);
|
||||||
|
shader.fs(`
|
||||||
|
void main() {
|
||||||
|
vec2 coord = v_texCoord;
|
||||||
|
if (coord.y > 0.25 && coord.y < 0.35) {
|
||||||
|
coord.y = sin((coord.y - 0.25) * 3.1415926535 / 0.2) * 0.1 + 0.25;
|
||||||
|
}
|
||||||
|
gl_FragColor = texture2D(u_sampler, coord);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
layer.requestAfterFrame(() => {
|
layer.requestAfterFrame(() => {
|
||||||
hero.setImage(core.material.images.images['hero2.png']);
|
hero.setImage(core.material.images.images['hero2.png']);
|
||||||
|
521
src/core/render/shader.ts
Normal file
521
src/core/render/shader.ts
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
import { logger } from '../common/logger';
|
||||||
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
|
import { isWebGL2Supported } from '../fx/webgl';
|
||||||
|
import { Container } from './container';
|
||||||
|
import { ERenderItemEvent, RenderItemPosition } from './item';
|
||||||
|
import { Transform } from './transform';
|
||||||
|
|
||||||
|
const SHADER_VERTEX_PREFIX_300 = /* glsl */ `#version 300 es
|
||||||
|
`;
|
||||||
|
const SHADER_VERTEX_PREFIX_100 = /* glsl */ `
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision highp float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
|
varying highp vec2 v_texCoord;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SHADER_FRAGMENT_PREFIX_300 = /* glsl */ `#version 300 es
|
||||||
|
`;
|
||||||
|
const SHADER_FRAGMENT_PREFIX_100 = /* glsl */ `
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision highp float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying highp vec2 v_texCoord;
|
||||||
|
|
||||||
|
uniform sampler2D u_sampler;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DEFAULT_VS = /* glsl */ `
|
||||||
|
void main() {
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
gl_Position = a_position;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const DEFAULT_FS = /* glsl */ `
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(u_sampler, v_texCoord);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type ShaderGLSLVersion = '100' | '300';
|
||||||
|
|
||||||
|
interface CompiledShader {
|
||||||
|
vertex: WebGLShader;
|
||||||
|
fragment: WebGLShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShaderBuffer {
|
||||||
|
position: WebGLBuffer;
|
||||||
|
texture: WebGLBuffer;
|
||||||
|
indices: WebGLBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShaderUniform {
|
||||||
|
u_sampler: WebGLUniformLocation;
|
||||||
|
|
||||||
|
[x: string]: WebGLUniformLocation | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShaderAttribute {
|
||||||
|
a_texCoord: number;
|
||||||
|
a_position: number;
|
||||||
|
|
||||||
|
[x: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShaderProgram {
|
||||||
|
program: WebGLProgram | null;
|
||||||
|
shader: CompiledShader | null;
|
||||||
|
uniform: ShaderUniform | null;
|
||||||
|
attribute: ShaderAttribute | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EShaderEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Shader extends Container<EShaderEvent> {
|
||||||
|
/** 是否支持此组件 */
|
||||||
|
static readonly support: boolean = isWebGL2Supported();
|
||||||
|
|
||||||
|
// 会用到的静态常量
|
||||||
|
static readonly VERTEX_SHADER: number = 0b1;
|
||||||
|
static readonly FRAGMENT_SHADER: number = 0b10;
|
||||||
|
|
||||||
|
private version: ShaderGLSLVersion = '100';
|
||||||
|
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
gl: WebGL2RenderingContext;
|
||||||
|
|
||||||
|
/** 是否修改过着色器,用于特定场景优化 */
|
||||||
|
private shaderModified: boolean = false;
|
||||||
|
/** 需要重新编译的着色器 */
|
||||||
|
private shaderDirty: boolean = true;
|
||||||
|
/** 当前程序是否被外部调用过 */
|
||||||
|
private programExterned: boolean = false;
|
||||||
|
|
||||||
|
/** 顶点着色器 */
|
||||||
|
private vertex: string = DEFAULT_VS;
|
||||||
|
/** 片元着色器 */
|
||||||
|
private fragment: string = DEFAULT_FS;
|
||||||
|
|
||||||
|
/** webgl使用的程序 */
|
||||||
|
private program: WebGLProgram | null = null;
|
||||||
|
/** 子元素构成的纹理 */
|
||||||
|
private texture: WebGLTexture | null = null;
|
||||||
|
/** 编译过的着色器,在切换着色器时会被删除 */
|
||||||
|
private shader: CompiledShader | null = null;
|
||||||
|
/** 缓冲区 */
|
||||||
|
private buffer: ShaderBuffer | null = null;
|
||||||
|
/** uniform */
|
||||||
|
private uniform: ShaderUniform | null = null;
|
||||||
|
/** attribute */
|
||||||
|
private attribute: ShaderAttribute | null = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
type: RenderItemPosition = 'static',
|
||||||
|
version: ShaderGLSLVersion = '100'
|
||||||
|
) {
|
||||||
|
super(type, !Shader.support);
|
||||||
|
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.gl = this.canvas.getContext('webgl2')!;
|
||||||
|
this.setVersion(version);
|
||||||
|
if (!Shader.support) {
|
||||||
|
this.canvas.width = 0;
|
||||||
|
this.canvas.height = 0;
|
||||||
|
}
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {
|
||||||
|
if (!Shader.support || !this.shaderModified) {
|
||||||
|
super.render(canvas, transform);
|
||||||
|
} else {
|
||||||
|
if (this.shaderDirty) {
|
||||||
|
this.cacheDirty = true;
|
||||||
|
this.compileShader();
|
||||||
|
this.shaderDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cacheDirty) {
|
||||||
|
const { ctx } = this.cache;
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.save();
|
||||||
|
super.render(this.cache, transform);
|
||||||
|
ctx.restore();
|
||||||
|
this.drawScene();
|
||||||
|
this.cacheDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.ctx.drawImage(this.canvas, 0, 0, this.width, this.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setHD(hd: boolean): void {
|
||||||
|
super.setHD(hd);
|
||||||
|
this.sizeGL(this.width, this.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
size(width: number, height: number): void {
|
||||||
|
super.size(width, height);
|
||||||
|
this.sizeGL(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sizeGL(width: number, height: number) {
|
||||||
|
const ratio = this.highResolution ? devicePixelRatio : 1;
|
||||||
|
const scale = ratio * core.domStyle.scale;
|
||||||
|
this.canvas.width = width * scale;
|
||||||
|
this.canvas.height = height * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawScene() {
|
||||||
|
const gl = this.gl;
|
||||||
|
if (!this.uniform || !gl) return;
|
||||||
|
// 清空画布
|
||||||
|
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
|
gl.clearColor(0, 0, 0, 0);
|
||||||
|
gl.clearDepth(1);
|
||||||
|
gl.enable(gl.DEPTH_TEST);
|
||||||
|
gl.depthFunc(gl.LEQUAL);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// 设置顶点信息
|
||||||
|
this.setPositionAttrib();
|
||||||
|
this.setTextureAttrib();
|
||||||
|
|
||||||
|
// 准备绘制
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer!.indices);
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||||
|
gl.texImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
this.cache.canvas
|
||||||
|
);
|
||||||
|
gl.uniform1i(this.uniform.uSampler, 0);
|
||||||
|
|
||||||
|
// 绘制
|
||||||
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换着色器程序
|
||||||
|
* @param program 着色器程序
|
||||||
|
*/
|
||||||
|
useProgram(program: ShaderProgram) {
|
||||||
|
this.program = program.program;
|
||||||
|
this.shader = program.shader;
|
||||||
|
this.uniform = program.uniform;
|
||||||
|
this.attribute = program.attribute;
|
||||||
|
this.gl?.useProgram(this.program);
|
||||||
|
this.programExterned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的着色器程序
|
||||||
|
*/
|
||||||
|
getProgram(): ShaderProgram {
|
||||||
|
this.programExterned = true;
|
||||||
|
return {
|
||||||
|
program: this.program,
|
||||||
|
shader: this.shader,
|
||||||
|
uniform: this.uniform,
|
||||||
|
attribute: this.attribute
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定的着色器程序
|
||||||
|
* @param program 着色器程序
|
||||||
|
*/
|
||||||
|
deleteProgram(program: ShaderProgram) {
|
||||||
|
if (!this.gl) return;
|
||||||
|
if (this.program === program.program) {
|
||||||
|
this.gl.useProgram(null);
|
||||||
|
this.program = null;
|
||||||
|
this.shader = null;
|
||||||
|
this.uniform = null;
|
||||||
|
this.attribute = null;
|
||||||
|
}
|
||||||
|
if (program.program) {
|
||||||
|
this.gl.deleteProgram(program.program);
|
||||||
|
}
|
||||||
|
if (program.shader) {
|
||||||
|
this.gl.deleteShader(program.shader.vertex);
|
||||||
|
this.gl.deleteShader(program.shader.fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置着色器使用的glsl版本,默认使用100版本
|
||||||
|
*/
|
||||||
|
setVersion(version: ShaderGLSLVersion) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置顶点着色器
|
||||||
|
* @param shader 着色器
|
||||||
|
*/
|
||||||
|
vs(shader: string) {
|
||||||
|
this.vertex = shader;
|
||||||
|
this.shaderModified = true;
|
||||||
|
this.shaderDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置片元着色器
|
||||||
|
* @param shader 着色器
|
||||||
|
*/
|
||||||
|
fs(shader: string) {
|
||||||
|
this.fragment = shader;
|
||||||
|
this.shaderModified = true;
|
||||||
|
this.shaderDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译指定着色器并附加到新程序,老程序可以在调用此函数之前通过 {@link Shader.getProgram} 获取,
|
||||||
|
* 否则,老程序将会被删除
|
||||||
|
*/
|
||||||
|
compileShader() {
|
||||||
|
if (!Shader.support) return;
|
||||||
|
|
||||||
|
const program = Shader.compileProgram(
|
||||||
|
this,
|
||||||
|
this.version,
|
||||||
|
this.vertex,
|
||||||
|
this.fragment
|
||||||
|
);
|
||||||
|
if (!program) return;
|
||||||
|
|
||||||
|
if (!this.programExterned) {
|
||||||
|
Shader.deleteProgram(this, this.getProgram());
|
||||||
|
}
|
||||||
|
this.programExterned = false;
|
||||||
|
|
||||||
|
this.program = program.program;
|
||||||
|
this.shader = program.shader;
|
||||||
|
this.uniform = program.uniform;
|
||||||
|
this.attribute = program.attribute;
|
||||||
|
this.gl?.useProgram(this.program);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 初始化部分
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
if (!this.gl) return;
|
||||||
|
this.program = this.gl.createProgram();
|
||||||
|
this.initTexture();
|
||||||
|
this.initBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initTexture() {
|
||||||
|
const gl = this.gl;
|
||||||
|
if (!gl) return;
|
||||||
|
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.texImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
this.cache.canvas
|
||||||
|
);
|
||||||
|
gl.generateMipmap(gl.TEXTURE_2D);
|
||||||
|
|
||||||
|
this.texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTextureAttrib() {
|
||||||
|
if (!this.attribute) return;
|
||||||
|
const gl = this.gl;
|
||||||
|
const pos = this.attribute.a_texCoord;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer!.texture);
|
||||||
|
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
gl.enableVertexAttribArray(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setPositionAttrib() {
|
||||||
|
if (!this.attribute) return;
|
||||||
|
const gl = this.gl;
|
||||||
|
const pos = this.attribute.a_position;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer!.position);
|
||||||
|
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
gl.enableVertexAttribArray(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initBuffers() {
|
||||||
|
const positions = new Float32Array([1, -1, -1, -1, 1, 1, -1, 1]);
|
||||||
|
const posBuffer = this.initBuffer(positions);
|
||||||
|
const textureCoord = new Float32Array([1, 1, 0, 1, 1, 0, 0, 0]);
|
||||||
|
const textureBuffer = this.initBuffer(textureCoord);
|
||||||
|
|
||||||
|
this.buffer = {
|
||||||
|
position: posBuffer!,
|
||||||
|
texture: textureBuffer!,
|
||||||
|
indices: this.initIndexBuffer()!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private initBuffer(pos: Float32Array) {
|
||||||
|
const gl = this.gl;
|
||||||
|
if (!gl) return;
|
||||||
|
const posBuffer = gl.createBuffer()!;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, pos, gl.STATIC_DRAW);
|
||||||
|
|
||||||
|
return posBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private initIndexBuffer() {
|
||||||
|
const gl = this.gl;
|
||||||
|
if (!gl) return;
|
||||||
|
const buffer = gl.createBuffer()!;
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
|
||||||
|
const indices = new Uint16Array([0, 1, 2, 2, 3, 1]);
|
||||||
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 静态api部分
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传入着色器,编译出对应的程序,可以直接通过 {@link Shader.useProgram} 用于Shader组件
|
||||||
|
* @param vs 顶点着色器
|
||||||
|
* @param fs 片元着色器
|
||||||
|
*/
|
||||||
|
static compileProgram(
|
||||||
|
shader: Shader,
|
||||||
|
version: ShaderGLSLVersion,
|
||||||
|
vs: string,
|
||||||
|
fs: string
|
||||||
|
): ShaderProgram | null {
|
||||||
|
const gl = shader.gl;
|
||||||
|
if (!gl) return null;
|
||||||
|
|
||||||
|
const program = gl.createProgram();
|
||||||
|
if (!program) return null;
|
||||||
|
|
||||||
|
const vsPrefix =
|
||||||
|
version === '100'
|
||||||
|
? SHADER_VERTEX_PREFIX_100
|
||||||
|
: SHADER_VERTEX_PREFIX_300;
|
||||||
|
const fsPrefix =
|
||||||
|
version === '100'
|
||||||
|
? SHADER_FRAGMENT_PREFIX_100
|
||||||
|
: SHADER_FRAGMENT_PREFIX_300;
|
||||||
|
|
||||||
|
const vertexShader = this.compileShader(
|
||||||
|
gl,
|
||||||
|
gl.VERTEX_SHADER,
|
||||||
|
vsPrefix + vs
|
||||||
|
);
|
||||||
|
const fragmentShader = this.compileShader(
|
||||||
|
gl,
|
||||||
|
gl.FRAGMENT_SHADER,
|
||||||
|
fsPrefix + fs
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!vertexShader || !fragmentShader) return null;
|
||||||
|
|
||||||
|
gl.attachShader(program, vertexShader);
|
||||||
|
gl.attachShader(program, fragmentShader);
|
||||||
|
gl.linkProgram(program);
|
||||||
|
|
||||||
|
const aTexCoord = gl.getAttribLocation(program, 'a_texCoord');
|
||||||
|
const aPosition = gl.getAttribLocation(program, 'a_position');
|
||||||
|
const uSampler = gl.getUniformLocation(program, 'u_sampler');
|
||||||
|
|
||||||
|
if (!uSampler) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
program,
|
||||||
|
attribute: { a_position: aPosition, a_texCoord: aTexCoord },
|
||||||
|
uniform: { u_sampler: uSampler },
|
||||||
|
shader: { vertex: vertexShader, fragment: fragmentShader }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static compileShader(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
type: number,
|
||||||
|
source: string
|
||||||
|
): WebGLShader | null {
|
||||||
|
const shader = gl.createShader(type);
|
||||||
|
if (!shader) return null;
|
||||||
|
gl.shaderSource(shader, source);
|
||||||
|
gl.compileShader(shader);
|
||||||
|
|
||||||
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||||
|
logger.error(
|
||||||
|
13,
|
||||||
|
`Cannot compile ${
|
||||||
|
type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'
|
||||||
|
} shader. Error info: ${gl.getShaderInfoLog(shader)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除着色器程序
|
||||||
|
* @param program 要删除的程序
|
||||||
|
*/
|
||||||
|
static deleteProgram(shader: Shader, program: ShaderProgram) {
|
||||||
|
const gl = shader.gl;
|
||||||
|
if (!gl) return;
|
||||||
|
gl.deleteProgram(program.program);
|
||||||
|
gl.deleteShader(program.shader?.vertex ?? null);
|
||||||
|
gl.deleteShader(program.shader?.fragment ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个程序中对应属性名的位置
|
||||||
|
* @param program 要获取的程序
|
||||||
|
* @param name 要获取的属性名
|
||||||
|
*/
|
||||||
|
static getAttribute(
|
||||||
|
shader: Shader,
|
||||||
|
program: ShaderProgram,
|
||||||
|
name: string
|
||||||
|
): number | null {
|
||||||
|
const gl = shader.gl;
|
||||||
|
if (!gl || !program.program || !program.attribute) return null;
|
||||||
|
if (program.attribute[name]) return program.attribute[name];
|
||||||
|
const loc = gl.getAttribLocation(program.program, name);
|
||||||
|
return (program.attribute[name] = loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个程序中对应uniform变量名的位置
|
||||||
|
* @param program 要获取的程序
|
||||||
|
* @param name 要获取的uniform变量名
|
||||||
|
*/
|
||||||
|
static getUniform(
|
||||||
|
shader: Shader,
|
||||||
|
program: ShaderProgram,
|
||||||
|
name: string
|
||||||
|
): WebGLUniformLocation | null {
|
||||||
|
const gl = shader.gl;
|
||||||
|
if (!gl || !program.program || !program.uniform) return null;
|
||||||
|
if (name in program.uniform) return program.uniform[name];
|
||||||
|
const loc = gl.getUniformLocation(program.program, name);
|
||||||
|
return (program.uniform[name] = loc);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user