Skip to content

Commit 03d2fa3

Browse files
committed
feat: add uniform buffer object support
1 parent aa2a3c1 commit 03d2fa3

File tree

5 files changed

+274
-2
lines changed

5 files changed

+274
-2
lines changed

examples/uniformBufferObject.html

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport"
6+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7+
<title>Hilo3d Uniform Buffer Object</title>
8+
<link rel="stylesheet" type="text/css" href="./example.css">
9+
</head>
10+
<body>
11+
<div id="container"></div>
12+
<script src="../build/Hilo3d.js"></script>
13+
<script src="./js/stats.js"></script>
14+
<script src="./js/OrbitControls.js"></script>
15+
<script src="./js/init.js"></script>
16+
<script>
17+
renderer.clearColor = new Hilo3d.Color(0, 0, 0, 1);
18+
renderer.preferWebGL2 = true;
19+
var container = new Hilo3d.Node();
20+
var geometry = new Hilo3d.SphereGeometry({
21+
radius: 1,
22+
heightSegments: 32,
23+
widthSegments: 64,
24+
})
25+
var modelUniformBuffer = new Hilo3d.UniformBuffer(new Float32Array(16));
26+
var materialUniformBuffer = new Hilo3d.UniformBuffer(new Float32Array(8));
27+
28+
var mesh = new Hilo3d.Mesh({
29+
rotationX:90,
30+
time:0,
31+
geometry: geometry,
32+
material: new Hilo3d.ShaderMaterial({
33+
shaderCacheId: "UVAnimation",
34+
getCustomRenderOption(option){
35+
option.CUSTOM_OPTION = 1;
36+
return option;
37+
},
38+
needBasicUnifroms: false,
39+
needBasicAttributes: false,
40+
diffuse: new Hilo3d.LazyTexture({
41+
src: '//gw.alicdn.com/tfs/TB1Q8dQSVXXXXciXVXXXXXXXXXX-512-512.jpg'
42+
}),
43+
mixTexture:new Hilo3d.LazyTexture({
44+
src: '//gw.alicdn.com/tfs/TB1T1wEizTpK1RjSZKPXXa3UpXa-512-512.png'
45+
}),
46+
uniforms:{
47+
u_diffuse:'DIFFUSE',
48+
u_mixTexture:{
49+
get(mesh, material, programInfo) {
50+
return Hilo3d.semantic.handlerTexture(material.mixTexture, programInfo.textureIndex);
51+
}
52+
},
53+
},
54+
uniformBlocks: {
55+
MaterialBlock: materialUniformBuffer,
56+
ModelBlock: modelUniformBuffer,
57+
},
58+
attributes:{
59+
a_position: 'POSITION',
60+
a_texcoord0:'TEXCOORD_0'
61+
},
62+
fs:`
63+
precision HILO_MAX_FRAGMENT_PRECISION float;
64+
65+
varying vec2 v_texcoord0;
66+
67+
uniform sampler2D u_diffuse;
68+
uniform sampler2D u_mixTexture;
69+
70+
layout(std140) uniform MaterialBlock {
71+
vec4 color;
72+
float u_time;
73+
};
74+
75+
void main(void) {
76+
float uOffset = sin(u_time * 0.0005);
77+
float vOffset = cos(u_time * 0.0003);
78+
vec4 diffuse = texture2D(u_diffuse, vec2(v_texcoord0.x + uOffset, v_texcoord0.y + vOffset));
79+
vec4 mixTexture = texture2D(u_mixTexture, v_texcoord0);
80+
gl_FragColor = mix(vec4(diffuse.r, diffuse.g, color.b, 1), mixTexture, 0.05);
81+
}
82+
`,
83+
vs:`
84+
precision HILO_MAX_VERTEX_PRECISION float;
85+
86+
attribute vec3 a_position;
87+
attribute vec2 a_texcoord0;
88+
89+
layout(std140) uniform ModelBlock {
90+
uniform mat4 u_modelViewProjectionMatrix;
91+
};
92+
93+
varying vec2 v_texcoord0;
94+
95+
void main(void) {
96+
vec4 pos = vec4(a_position, 1.0);
97+
gl_Position = u_modelViewProjectionMatrix * pos;
98+
v_texcoord0 = a_texcoord0;
99+
}
100+
`
101+
}),
102+
onUpdate(dt){
103+
this.time += dt;
104+
Hilo3d.semantic.init(renderer, {}, stage.camera, renderer.lightManager, renderer.fog);
105+
106+
materialUniformBuffer.data.set([1, 0, Math.sin(this.time * 0.001) * 0.5 + 0.5, 1], 0);
107+
materialUniformBuffer.data[4] = this.time;
108+
materialUniformBuffer.isDirty = true;
109+
110+
modelUniformBuffer.data.set(Hilo3d.semantic.MODELVIEWPROJECTION.get(this), 0);
111+
modelUniformBuffer.isDirty = true;
112+
}
113+
});
114+
115+
container.addChild(mesh);
116+
stage.addChild(container);
117+
118+
// stage.renderer.initContext();
119+
// var material = mesh.material;
120+
121+
// // 编译
122+
// var shader = Hilo3d.Shader.getCustomShader(material.vs, material.fs, '', material.shaderCacheId);
123+
// shader.alwaysUse = true;
124+
// var program = Hilo3d.Program.getProgram(, Hilo3d.Shader.renderer.state);
125+
// program.alwaysUse = true;
126+
127+
// // 删除
128+
// var shader = Hilo3d.Shader.cache.get(material.shaderCacheId);
129+
// var program = Hilo3d.Program.cache.get(shader.id);
130+
// shader.destroy();
131+
// program.destroy();
132+
</script>
133+
</body>
134+
</html>

src/Hilo3d.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export { default as Camera } from './camera/Camera';
2323
export { default as PerspectiveCamera } from './camera/PerspectiveCamera';
2424
export { default as OrthographicCamera } from './camera/OrthographicCamera';
2525
export { default as Buffer } from './renderer/Buffer';
26+
export { default as UniformBuffer } from './renderer/UniformBuffer';
2627
export { default as capabilities } from './renderer/capabilities';
2728
export { default as extensions } from './renderer/extensions';
2829
export { default as Framebuffer } from './renderer/Framebuffer';

src/renderer/Program.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ const Program = Class.create(/** @lends Program.prototype */ {
113113
*/
114114
uniforms: null,
115115

116+
/**
117+
* uniformBlock 集合
118+
* @type {Object}
119+
* @default null
120+
*/
121+
uniformBlocks: null,
122+
116123
/**
117124
* program
118125
* @type {WebGLProgram}
@@ -164,6 +171,7 @@ const Program = Class.create(/** @lends Program.prototype */ {
164171

165172
this.attributes = {};
166173
this.uniforms = {};
174+
this.uniformBlocks = {};
167175
this.gl = this.state.gl;
168176
this.isWebGL2 = this.state.isWebGL2;
169177
this.program = this.createProgram();
@@ -357,8 +365,34 @@ const Program = Class.create(/** @lends Program.prototype */ {
357365
initUniforms() {
358366
const gl = this.gl;
359367
const program = this.program;
368+
const uniforms = this.uniforms;
369+
const uniformBlocks = this.uniformBlocks;
360370

361371
const num = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
372+
let uniformBlockIndices;
373+
if (this.isWebGL2) {
374+
const blockNum = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS);
375+
for (let i = 0; i < blockNum; i++) {
376+
const blockName = gl.getActiveUniformBlockName(program, i);
377+
const blockIndex = gl.getUniformBlockIndex(program, blockName);
378+
gl.uniformBlockBinding(program, blockIndex, i);
379+
uniformBlocks[blockName] = {
380+
blockIndex,
381+
};
382+
Object.defineProperty(this, blockName, {
383+
set: (uniformBuffer) => {
384+
gl.bindBufferBase(gl.UNIFORM_BUFFER, i, uniformBuffer.getBuffer(gl).buffer);
385+
}
386+
});
387+
}
388+
389+
let uniformIndices = [];
390+
for (let i = 0; i < num; i++) {
391+
uniformIndices.push(i);
392+
}
393+
uniformBlockIndices = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_BLOCK_INDEX);
394+
}
395+
362396
let textureIndex = 0;
363397
for (let i = 0; i < num; i++) {
364398
let {
@@ -367,6 +401,11 @@ const Program = Class.create(/** @lends Program.prototype */ {
367401
type
368402
} = gl.getActiveUniform(program, i);
369403

404+
// uniform block index -1 说明不是 uniform block
405+
if (uniformBlockIndices && uniformBlockIndices[i] !== -1) {
406+
continue;
407+
}
408+
370409
name = name.replace(/\[0\]$/, '');
371410
const location = gl.getUniformLocation(program, name);
372411
const glTypeInfo = glType.get(type);
@@ -375,7 +414,7 @@ const Program = Class.create(/** @lends Program.prototype */ {
375414
uniform
376415
} = glTypeInfo;
377416

378-
this.uniforms[name] = {
417+
uniforms[name] = {
379418
name,
380419
location,
381420
type,
@@ -384,7 +423,7 @@ const Program = Class.create(/** @lends Program.prototype */ {
384423
};
385424

386425
if (type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) {
387-
this.uniforms[name].textureIndex = textureIndex;
426+
uniforms[name].textureIndex = textureIndex;
388427
textureIndex += size;
389428
}
390429

@@ -422,6 +461,7 @@ const Program = Class.create(/** @lends Program.prototype */ {
422461

423462
this.gl.deleteProgram(this.program);
424463
this.uniforms = null;
464+
this.uniformBlocks = null;
425465
this.attributes = null;
426466
this.program = null;
427467
this.gl = null;

src/renderer/UniformBuffer.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Class from '../core/Class';
2+
import Buffer from './Buffer';
3+
4+
/**
5+
* Uniform Buffer Object
6+
* @class
7+
*/
8+
const UniformBuffer = Class.create(/** @lends UniformBuffer.prototype */ {
9+
/**
10+
* @default Buffer
11+
* @type {String}
12+
*/
13+
className: 'UniformBuffer',
14+
15+
/**
16+
* @default true
17+
* @type {Boolean}
18+
*/
19+
isUniformBuffer: true,
20+
21+
/**
22+
* is dirty
23+
* @type {Boolean}
24+
* @default false
25+
*/
26+
isDirty: false,
27+
28+
/**
29+
* data
30+
* @type {TypedArray|ArrayBuffer}
31+
* @default null
32+
*/
33+
data: {
34+
get() {
35+
return this._data;
36+
},
37+
set(data) {
38+
this._data = data;
39+
this.isDirty = true;
40+
}
41+
},
42+
43+
/**
44+
* data
45+
* @type {TypedArray|ArrayBuffer}
46+
* @default null
47+
* @private
48+
*/
49+
_data: null,
50+
51+
/**
52+
* @type {Buffer}
53+
* @default null
54+
* @private
55+
*/
56+
_buffer: null,
57+
58+
/**
59+
* @constructs
60+
*/
61+
constructor(data) {
62+
this.data = data;
63+
},
64+
65+
getBuffer(gl) {
66+
if (!this._buffer) {
67+
this._buffer = new Buffer(gl, gl.UNIFORM_BUFFER, null, gl.DYNAMIC_DRAW);
68+
}
69+
70+
if (this.isDirty) {
71+
this._buffer.bufferData(this.data);
72+
this.isDirty = false;
73+
}
74+
return this._buffer;
75+
},
76+
77+
destroy() {
78+
if (this._buffer) {
79+
this._buffer.destroy();
80+
this._buffer = null;
81+
}
82+
this.isDirty = true;
83+
},
84+
});
85+
86+
export default UniformBuffer;

src/renderer/WebGLRenderer.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,17 @@ const WebGLRenderer = Class.create(/** @lends WebGLRenderer.prototype */ {
617617
*/
618618
setupUniforms(program, mesh, useInstanced, force) {
619619
const material = this.forceMaterial || mesh.material;
620+
621+
if (this.isWebGL2) {
622+
const uniformBlocks = material.uniformBlocks;
623+
for (let name in program.uniformBlocks) {
624+
const uniformBlock = uniformBlocks[name];
625+
if (uniformBlock) {
626+
program[name] = uniformBlock;
627+
}
628+
}
629+
}
630+
620631
for (let name in program.uniforms) {
621632
const uniformInfo = material.getUniformInfo(name);
622633
const programUniformInfo = program.uniforms[name];

0 commit comments

Comments
 (0)