forked from playcanvas/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransform-feedback.js
More file actions
164 lines (153 loc) · 7.27 KB
/
transform-feedback.js
File metadata and controls
164 lines (153 loc) · 7.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
pc.extend(pc, function () {
'use strict';
/**
* @name pc.TransformFeedback
* @class Transform feedback helper object
* @description This object allows you to configure and use the transform feedback feature (WebGL2 only).
* How to use:<br>
* <ol>
* <li>First, check that you're on WebGL2, by looking at the <code>app.graphicsDevice.webgl2</code> value.</li>
* <li>Define the outputs in your vertex shader. The syntax is <code>out vec3 out_vertex_position</code>, note that there must be out_ in the name. You can then simply assign values to these outputs in VS. The order and size of shader outputs must match the output buffer layout.</li>
* <li>Create the shader using <code>pc.TransformFeedback.createShader(device, vsCode, yourShaderName)</code>.</li>
* <li>Create/acquire the input vertex buffer. Can be any pc.VertexBuffer, either manually created, or from a pc.Mesh.</li>
* <li>Create the pc.TransformFeedback object: <code>var tf = new pc.TransformFeedback(inputBuffer)</code>. This object will internally create an output buffer.</li>
* <li>Run the shader: <code>tf.process(shader)</code>. Shader will take the input buffer, process it and write to the output buffer, then the input/output buffers will be automatically swapped, so you'll immediately see the result.</li>
* </ol>
* @example
* // *** shader asset ***
* attribute vec3 vertex_position;
* attribute vec3 vertex_normal;
* attribute vec2 vertex_texCoord0;
* attribute vec4 vertex_tangent;
* out vec3 out_vertex_position;
* out vec3 out_vertex_normal;
* out vec2 out_vertex_texCoord0;
* out vec4 out_vertex_tangent;
* void main(void) {
* // read position and normal, write new position (push away)
* out_vertex_position = vertex_position + vertex_normal * 0.01;
* // pass other attributes unchanged
* out_vertex_normal = vertex_normal;
* out_vertex_texCoord0 = vertex_texCoord0;
* out_vertex_tangent = vertex_tangent;
* }
* @example
* // *** script asset ***
* var TransformExample = pc.createScript('transformExample');
*
* // attribute that references shader asset and material
* TransformExample.attributes.add('shaderCode', { type: 'asset', assetType: 'shader' });
* TransformExample.attributes.add('material', { type: 'asset', assetType: 'material' });
*
* TransformExample.prototype.initialize = function() {
* var device = this.app.graphicsDevice;
* var mesh = pc.createTorus(device, { tubeRadius: 0.01, ringRadius: 3 });
* var node = new pc.GraphNode();
* var meshInstance = new pc.MeshInstance(node, mesh, this.material.resource);
* var model = new pc.Model();
* model.graph = node;
* model.meshInstances = [ meshInstance ];
* this.app.scene.addModel(model);
*
* // if webgl2 is not supported, TF is not available
* if (!device.webgl2) return;
* var inputBuffer = mesh.vertexBuffer;
* this.tf = new pc.TransformFeedback(inputBuffer);
* this.shader = pc.TransformFeedback.createShader(device, this.shaderCode.resource, "tfMoveUp");
* };
*
* TransformExample.prototype.update = function(dt) {
* if (!this.app.graphicsDevice.webgl2) return;
* this.tf.process(this.shader);
* };
* @param {pc.VertexBuffer} inputBuffer The input vertex buffer
* @param {Number} [usage] The optional usage type of the output vertex buffer (see pc.BUFFER_*). pc.BUFFER_GPUDYNAMIC is recommended for continuous update, and is the default value.
*/
var TransformFeedback = function (inputBuffer, usage) {
usage = usage || pc.BUFFER_GPUDYNAMIC;
this.device = inputBuffer.device;
var gl = this.device.gl;
this._inputBuffer = inputBuffer;
if (usage === pc.BUFFER_GPUDYNAMIC && inputBuffer.usage !== usage) {
// have to recreate input buffer with other usage
gl.bindBuffer(gl.ARRAY_BUFFER, inputBuffer.bufferId);
gl.bufferData(gl.ARRAY_BUFFER, inputBuffer.storage, gl.DYNAMIC_COPY);
}
this._outputBuffer = new pc.VertexBuffer(inputBuffer.device, inputBuffer.format, inputBuffer.numVertices, usage, inputBuffer.storage);
};
/**
* @function
* @name pc.TransformFeedback#createShader
* @description Creates a transform feedback ready vertex shader from code.
* @param {pc.GraphicsDevice} graphicsDevice The graphics device used by the renderer.
* @param {String} vsCode Vertex shader code. Should contain output variables starting with "out_".
* @param {String} name Unique name for caching the shader.
* @returns {pc.Shader} A shader to use in the process() function.
*/
TransformFeedback.createShader = function (device, vsCode, name) {
return pc.shaderChunks.createShaderFromCode(device, vsCode, null, name, true);
};
TransformFeedback.prototype = {
/**
* @function
* @name pc.TransformFeedback#destroy
* @description Destroys the transform feedback helper object
*/
destroy: function () {
this._outputBuffer.destroy();
},
/**
* @function
* @name pc.TransformFeedback#process
* @description Runs the specified shader on the input buffer, writes results into the new buffer, then optionally swaps input/output.
* @param {pc.Shader} shader A vertex shader to run. Should be created with pc.TransformFeedback.createShader.
* @param {Boolean} [swap] Swap input/output buffer data. Useful for continuous buffer processing. Default is true.
*/
process: function (shader, swap) {
if (swap === undefined) swap = true;
var device = this.device;
device.setRenderTarget(null);
device.updateBegin();
device.setVertexBuffer(this._inputBuffer, 0);
device.setRaster(false);
device.setTransformFeedbackBuffer(this._outputBuffer);
device.setShader(shader);
device.draw({
type: pc.PRIMITIVE_POINTS,
base: 0,
count: this._inputBuffer.numVertices,
indexed: false
});
device.setTransformFeedbackBuffer(null);
device.setRaster(true);
device.updateEnd();
// swap buffers
if (swap) {
var tmp = this._inputBuffer.bufferId;
this._inputBuffer.bufferId = this._outputBuffer.bufferId;
this._outputBuffer.bufferId = tmp;
}
}
};
/**
* @readonly
* @name pc.TransformFeedback#inputBuffer
* @type pc.VertexBuffer
* @description The current input buffer
*/
Object.defineProperty(TransformFeedback.prototype, 'inputBuffer', {
get: function () { return this._inputBuffer; },
});
/**
* @readonly
* @name pc.TransformFeedback#outputBuffer
* @type pc.VertexBuffer
* @description The current output buffer
*/
Object.defineProperty(TransformFeedback.prototype, 'outputBuffer', {
get: function () { return this._outputBuffer; },
});
return {
TransformFeedback: TransformFeedback
};
}());