粒子发射器的 webGL 制服问题:"uniform1f: location not for current program"
Problem with webGL uniforms for particle emitter : "uniform1f: location not for current program"
我用 WebGL/JS 创建了一个粒子发射器 class,它在一个实例中工作正常。
问题是当我尝试创建该对象的 2 个实例时。
我从 GPU 收到一条错误消息:
WebGL:INVALID_OPERATION:uniform1f:位置不适用于当前程序
所以,我知道这是着色器程序的问题link:每个统一位置一个程序。但是在对该主题进行多次搜索之后,我无法找到解决方案。我对统一位置和属性缓冲区的概念不够熟悉,无法理解该错误。
这是我的 class 的代码。这两个实例位于代码的开头。
https://codepen.io/stephanemill/pen/KKXQJqG
你看到了什么:
- 第一个发射器启动
- 500 毫秒后,第二个发射器启动,然后,
-
- 停止执行第一个发射器,
-
- 抛出错误信息
window.addEventListener('load',()=>{
new Particle({
quantity: 2000,
timeFactor: 100,
lifetime: 10,
fadeOut: .2,
size: 2,
x: 50,
y: 70
});
setTimeout(()=>{
new Particle({
quantity: 2000,
timeFactor: 100,
lifetime: 5,
fadeOut: .2,
size: 2,
x: 50,
y: 50,
});
},500)
});
class Particle {
static id = 0;
static canvas = document.querySelector("#canvas-webgl");
constructor(settings){
settings = Object.assign({
// Nombre de particules
quantity: 200,
// Taille des particules
size: 2,
color: {
r: {
min: 255,
max: 255,
},
g: {
min: 255,
max: 255,
},
b: {
min: 0,
max: 0,
},
},
timeFactor: 1,
x: 50,
y: 50,
// Durée de vie
lifetime: 2,
// Durée du fadeout
fadeOut: 1,
speed: {
min: 0,
max: 5,
spread: 0,
sigma: .2,
},
angle: {
min: 0,
max: 90,
spread: 0,
sigma: .2,
},
gravity: 0,
texture: null,
}, settings);
Object.assign(this, settings);
this.canvas = this.constructor.canvas;
this.ctx = this.canvas.getContext("webgl");
this.width = this.canvas.width;
this.height = this.canvas.height;
this.ratio = this.width / this.height;
this.startTime = Date.now();
if(!this.ctx) {
alert("WebGL not supported!");
}
// Séquence de démarrage du flux
// -------------------------------
this.initProgram();
this.initAttributes();
this.initUniforms();
this.initBlending();
// this.initHUD();
this.autokill();
this.render();
this.constructor.id++;
return this;
}
initProgram(){
this.codeVertexShader = this.getShader('explosion');
this.codeFragmentShader = this.getFragmentShader();
// Création du vertex shader
this.vertexShader = this.ctx.createShader(this.ctx.VERTEX_SHADER);
this.ctx.shaderSource(this.vertexShader, this.codeVertexShader);
this.ctx.compileShader(this.vertexShader);
// Création du fragment shader
this.fragmentShader = this.ctx.createShader(this.ctx.FRAGMENT_SHADER);
this.ctx.shaderSource(this.fragmentShader, this.codeFragmentShader);
this.ctx.compileShader(this.fragmentShader);
// Création du programme
this.program = this.ctx.createProgram();
this.ctx.attachShader(this.program, this.vertexShader);
this.ctx.attachShader(this.program, this.fragmentShader);
this.ctx.linkProgram(this.program);
this.ctx.useProgram(this.program);
}
initAttributes(){
/*
--------------------------------------------
Mise en mémoire des attributs
--------------------------------------------
*/
const arraySpeedAngle = new Float32Array(this.quantity * 2);
const arrayPosition = new Float32Array(this.quantity * 2);
const arrayGravity = new Float32Array(this.quantity * 2);
const arrayColor = new Float32Array(this.quantity * 3);
for(let i=0; i<arraySpeedAngle.length; i+=2){
const angleMin = Math.deg2rad(this.angle.min);
const angleMax = Math.deg2rad(this.angle.max);
const angleSpread = Math.deg2rad(this.angle.spread);
const angle = Math.gradient(angleMin, angleMax, angleSpread, this.angle.sigma);
const speed = Math.gradient(this.speed.min, this.speed.max, this.speed.spread, this.speed.sigma);
arraySpeedAngle[i] = speed;
arraySpeedAngle[i+1] = angle;
}
for(let i=0; i<arrayPosition.length; i+=2){
let x0 = (this.x * 2 / 100) - 1;
let y0 = 1 - (this.y * 2 / 100);
x0 *= this.ratio;
arrayPosition[i] = x0;
arrayPosition[i+1] = y0;
}
for(let i=0; i<arrayGravity.length; i++){
arrayGravity[i] = this.gravity;
// arrayGravity[i] = Math.random()*10;
}
for(let i=0; i<arrayColor.length; i+=3){
const r = Math.rand(this.color.r.min, this.color.r.max);
const g = Math.rand(this.color.g.min, this.color.g.max);
const b = Math.rand(this.color.b.min, this.color.b.max);
arrayColor[i] = r/255;
arrayColor[i+1] = g/255;
arrayColor[i+2] = b/255;
}
// Création du tampon de données sur Vitesse et Angle
this.bufferSpeedAngle = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferSpeedAngle);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arraySpeedAngle, this.ctx.STATIC_DRAW);
const refSpeedAngle = this.ctx.getAttribLocation(this.program, "speed_angle");
this.ctx.vertexAttribPointer(refSpeedAngle, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refSpeedAngle);
// Création du tampon de données sur la Position
this.bufferPosition = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferPosition);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayPosition, this.ctx.STATIC_DRAW);
const refPosition = this.ctx.getAttribLocation(this.program, "position");
this.ctx.vertexAttribPointer(refPosition, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refPosition);
// Création du tampon de données sur la Gravité
this.bufferGravity = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferGravity);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayGravity, this.ctx.STATIC_DRAW);
const refGravity = this.ctx.getAttribLocation(this.program, "gravity");
this.ctx.vertexAttribPointer(refGravity, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refGravity);
// Création du tampon de données sur la Couleur
this.bufferColor = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferColor);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayColor, this.ctx.STATIC_DRAW);
const refColor = this.ctx.getAttribLocation(this.program, "color");
this.ctx.vertexAttribPointer(refColor, 3, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refColor);
// console.log(refSpeedAngle)
// console.log(refPosition)
// console.log(refGravity)
// console.log(refColor)
}
initUniforms(){
/*
--------------------------------------------
Mise en mémoire des uniforms
--------------------------------------------
*/
// Location of uniforms
this.refTime = this.ctx.getUniformLocation(this.program, "time");
this.refTimeFactor = this.ctx.getUniformLocation(this.program, "timeFactor");
this.refLifetime = this.ctx.getUniformLocation(this.program, "lifetime");
this.refFadeOut = this.ctx.getUniformLocation(this.program, "fadeOut");
this.refRatio = this.ctx.getUniformLocation(this.program, "ratio");
this.refSize = this.ctx.getUniformLocation(this.program, "size");
// Assig values to uniforms
this.ctx.uniform1f(this.refTimeFactor, this.timeFactor);
this.ctx.uniform1f(this.refLifetime, this.lifetime);
this.ctx.uniform1f(this.refFadeOut, this.fadeOut);
this.ctx.uniform1f(this.refRatio, this.ratio);
this.ctx.uniform1f(this.refSize, this.size);
// Uniform des textures
if(!this.texture)
return;
if(this.texture)
this.texture = document.querySelector(this.texture);
var texture = this.ctx.createTexture();
this.ctx.bindTexture(this.ctx.TEXTURE_2D, texture);
this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, this.texture);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST);
this.ctx.activeTexture(this.ctx.TEXTURE0);
this.refTexture = this.ctx.getUniformLocation(this.program, "texture");
this.ctx.uniform1i(this.textureLocation, 0);
}
initBlending(){
/*
--------------------------------------------
Paramètres de gestion des couleurs
--------------------------------------------
*/
this.ctx.enable(this.ctx.BLEND);
this.ctx.blendFunc(this.ctx.SRC_ALPHA,this.ctx.ONE_MINUS_SRC_ALPHA);
this.ctx.disable(this.ctx.DEPTH_TEST);
this.ctx.clearColor(0,0,0,0.0);
}
initHUD(){
document.querySelector('#nbr-particles span').innerHTML = this.quantity;
}
render() {
// Rendu du canvas webgl
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
let time = (Date.now() - this.startTime)/1000;
this.ctx.uniform1f(this.refTime, time);
this.ctx.drawArrays(this.ctx.POINTS, 0, this.quantity);
this.loopID = window.requestAnimationFrame(()=>{
this.render();
});
}
getShader(){
return `
precision mediump float;
attribute vec2 speed_angle;
attribute vec2 position;
attribute vec2 gravity;
attribute vec3 color;
uniform float time;
uniform float timeFactor;
uniform float ratio;
uniform float size;
varying lowp vec3 vColor;
void main(void) {
gl_PointSize = size;
vColor = color;
// float modTime = mod(time*10., 1.);
float modTime = time / timeFactor;
float speed = speed_angle.x;
float angle = speed_angle.y;
float G = gravity.x;
// if(sin((speed*(time))) > .5)
// gl_PointSize *= gl_PointSize * sin(speed);
float x0 = position.x;
float y0 = position.y;
// Equations horaires de la parabole
// ---------------------------------
float x = speed * cos(angle) * modTime + x0;
float y = -.5 * G * pow(modTime, 2.) + speed * sin(angle) * modTime + y0;
// Ratio canvas
// ---------------------------------
x /= ratio;
// x = x + sin(y)/.2;
gl_Position=vec4( x , y, 0.,1.);
}`;
}
getFragmentShader(){
return `
precision mediump float;
varying lowp vec3 vColor;
uniform float time;
uniform float lifetime;
uniform float fadeOut;
uniform sampler2D texture;
vec4 baseTexture;
void main(void) {
// float modTime = mod(T*10., 1.);
vec3 fragmentColor = vColor;
// float x = gl_PointCoord.x * sin(T*10.*V.x);
// float y = gl_PointCoord.y * sin(T*10.*V.y);
// vec2 coords = vec2(x,y);
// baseTexture = texture2D( texture, coords);
baseTexture = texture2D( texture, gl_PointCoord);
// fadeOut
// ----------------------------------
float opacity = 1.;
if(time > lifetime)
opacity -= (time - lifetime) / fadeOut;
// To the white
// ----------------------------------
float whiteDuration = .5;
if(time < whiteDuration){
float deltaColorR = (1. - fragmentColor.x) * (whiteDuration - time);
float deltaColorG = (1. - fragmentColor.y) * (whiteDuration - time);
float deltaColorB = (1. - fragmentColor.z) * (whiteDuration - time);
fragmentColor = vec3(fragmentColor.x + deltaColorR, fragmentColor.y + deltaColorG, fragmentColor.z + deltaColorB);
}
gl_FragColor = vec4(fragmentColor.x, fragmentColor.y, fragmentColor.z, opacity);
// gl_FragColor = vec4(vColor.x, vColor.y, vColor.z, opacity);
// gl_FragColor = vec4(.6, 1.0, 1.0, opacity);
// gl_FragColor = vec4(1.0, 1.0, 1., 1.);
// gl_FragColor = baseTexture * vec4(.7 * sin(V.x), .0, 1., 1.);
// gl_FragColor = baseTexture;
// rotation
// https://www.py4u.net/discuss/95325
}`;
}
autokill(){
setTimeout(()=>{
this.kill();
// myParticle();
}, (this.lifetime + this.fadeOut)*1000);
}
kill(){
this.ctx.bindTexture(this.ctx.TEXTURE_2D, null);
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, null);
this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, null);
this.ctx.bindRenderbuffer(this.ctx.RENDERBUFFER, null);
this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, null);
this.ctx.deleteBuffer(this.bufferSpeedAngle);
this.ctx.deleteBuffer(this.bufferPosition);
this.ctx.deleteBuffer(this.bufferGravity);
this.ctx.deleteProgram(this.program);
this.ctx.deleteShader(this.fragmentShader);
this.ctx.deleteShader(this.vertexShader);
//
window.cancelAnimationFrame(this.loopID);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
// console.clear();
// new Particle(1000);
}
} // End of class
您(不必要地)创建了两个着色器程序,每个实例化一个,但您只在 initProgram
方法中调用 useProgram
,但是调用 useProgram
会设置一个 global 呈现上下文的状态(在两种情况下都与从同一个 canvas 请求的相同)导致程序保持绑定状态直到更改。因此,当您使用第一个发射器进行渲染时,它会尝试使用第二个发射器(一旦创建)的着色器程序进行渲染。每次渲染时,您都需要 select 要渲染的程序,因此请在 render
方法中调用 useProgram
。
我用 WebGL/JS 创建了一个粒子发射器 class,它在一个实例中工作正常。 问题是当我尝试创建该对象的 2 个实例时。 我从 GPU 收到一条错误消息: WebGL:INVALID_OPERATION:uniform1f:位置不适用于当前程序
所以,我知道这是着色器程序的问题link:每个统一位置一个程序。但是在对该主题进行多次搜索之后,我无法找到解决方案。我对统一位置和属性缓冲区的概念不够熟悉,无法理解该错误。
这是我的 class 的代码。这两个实例位于代码的开头。 https://codepen.io/stephanemill/pen/KKXQJqG
你看到了什么:
- 第一个发射器启动
- 500 毫秒后,第二个发射器启动,然后,
-
- 停止执行第一个发射器,
-
- 抛出错误信息
window.addEventListener('load',()=>{
new Particle({
quantity: 2000,
timeFactor: 100,
lifetime: 10,
fadeOut: .2,
size: 2,
x: 50,
y: 70
});
setTimeout(()=>{
new Particle({
quantity: 2000,
timeFactor: 100,
lifetime: 5,
fadeOut: .2,
size: 2,
x: 50,
y: 50,
});
},500)
});
class Particle {
static id = 0;
static canvas = document.querySelector("#canvas-webgl");
constructor(settings){
settings = Object.assign({
// Nombre de particules
quantity: 200,
// Taille des particules
size: 2,
color: {
r: {
min: 255,
max: 255,
},
g: {
min: 255,
max: 255,
},
b: {
min: 0,
max: 0,
},
},
timeFactor: 1,
x: 50,
y: 50,
// Durée de vie
lifetime: 2,
// Durée du fadeout
fadeOut: 1,
speed: {
min: 0,
max: 5,
spread: 0,
sigma: .2,
},
angle: {
min: 0,
max: 90,
spread: 0,
sigma: .2,
},
gravity: 0,
texture: null,
}, settings);
Object.assign(this, settings);
this.canvas = this.constructor.canvas;
this.ctx = this.canvas.getContext("webgl");
this.width = this.canvas.width;
this.height = this.canvas.height;
this.ratio = this.width / this.height;
this.startTime = Date.now();
if(!this.ctx) {
alert("WebGL not supported!");
}
// Séquence de démarrage du flux
// -------------------------------
this.initProgram();
this.initAttributes();
this.initUniforms();
this.initBlending();
// this.initHUD();
this.autokill();
this.render();
this.constructor.id++;
return this;
}
initProgram(){
this.codeVertexShader = this.getShader('explosion');
this.codeFragmentShader = this.getFragmentShader();
// Création du vertex shader
this.vertexShader = this.ctx.createShader(this.ctx.VERTEX_SHADER);
this.ctx.shaderSource(this.vertexShader, this.codeVertexShader);
this.ctx.compileShader(this.vertexShader);
// Création du fragment shader
this.fragmentShader = this.ctx.createShader(this.ctx.FRAGMENT_SHADER);
this.ctx.shaderSource(this.fragmentShader, this.codeFragmentShader);
this.ctx.compileShader(this.fragmentShader);
// Création du programme
this.program = this.ctx.createProgram();
this.ctx.attachShader(this.program, this.vertexShader);
this.ctx.attachShader(this.program, this.fragmentShader);
this.ctx.linkProgram(this.program);
this.ctx.useProgram(this.program);
}
initAttributes(){
/*
--------------------------------------------
Mise en mémoire des attributs
--------------------------------------------
*/
const arraySpeedAngle = new Float32Array(this.quantity * 2);
const arrayPosition = new Float32Array(this.quantity * 2);
const arrayGravity = new Float32Array(this.quantity * 2);
const arrayColor = new Float32Array(this.quantity * 3);
for(let i=0; i<arraySpeedAngle.length; i+=2){
const angleMin = Math.deg2rad(this.angle.min);
const angleMax = Math.deg2rad(this.angle.max);
const angleSpread = Math.deg2rad(this.angle.spread);
const angle = Math.gradient(angleMin, angleMax, angleSpread, this.angle.sigma);
const speed = Math.gradient(this.speed.min, this.speed.max, this.speed.spread, this.speed.sigma);
arraySpeedAngle[i] = speed;
arraySpeedAngle[i+1] = angle;
}
for(let i=0; i<arrayPosition.length; i+=2){
let x0 = (this.x * 2 / 100) - 1;
let y0 = 1 - (this.y * 2 / 100);
x0 *= this.ratio;
arrayPosition[i] = x0;
arrayPosition[i+1] = y0;
}
for(let i=0; i<arrayGravity.length; i++){
arrayGravity[i] = this.gravity;
// arrayGravity[i] = Math.random()*10;
}
for(let i=0; i<arrayColor.length; i+=3){
const r = Math.rand(this.color.r.min, this.color.r.max);
const g = Math.rand(this.color.g.min, this.color.g.max);
const b = Math.rand(this.color.b.min, this.color.b.max);
arrayColor[i] = r/255;
arrayColor[i+1] = g/255;
arrayColor[i+2] = b/255;
}
// Création du tampon de données sur Vitesse et Angle
this.bufferSpeedAngle = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferSpeedAngle);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arraySpeedAngle, this.ctx.STATIC_DRAW);
const refSpeedAngle = this.ctx.getAttribLocation(this.program, "speed_angle");
this.ctx.vertexAttribPointer(refSpeedAngle, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refSpeedAngle);
// Création du tampon de données sur la Position
this.bufferPosition = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferPosition);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayPosition, this.ctx.STATIC_DRAW);
const refPosition = this.ctx.getAttribLocation(this.program, "position");
this.ctx.vertexAttribPointer(refPosition, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refPosition);
// Création du tampon de données sur la Gravité
this.bufferGravity = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferGravity);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayGravity, this.ctx.STATIC_DRAW);
const refGravity = this.ctx.getAttribLocation(this.program, "gravity");
this.ctx.vertexAttribPointer(refGravity, 2, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refGravity);
// Création du tampon de données sur la Couleur
this.bufferColor = this.ctx.createBuffer();
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.bufferColor);
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, arrayColor, this.ctx.STATIC_DRAW);
const refColor = this.ctx.getAttribLocation(this.program, "color");
this.ctx.vertexAttribPointer(refColor, 3, this.ctx.FLOAT, false, 0, 0);
this.ctx.enableVertexAttribArray(refColor);
// console.log(refSpeedAngle)
// console.log(refPosition)
// console.log(refGravity)
// console.log(refColor)
}
initUniforms(){
/*
--------------------------------------------
Mise en mémoire des uniforms
--------------------------------------------
*/
// Location of uniforms
this.refTime = this.ctx.getUniformLocation(this.program, "time");
this.refTimeFactor = this.ctx.getUniformLocation(this.program, "timeFactor");
this.refLifetime = this.ctx.getUniformLocation(this.program, "lifetime");
this.refFadeOut = this.ctx.getUniformLocation(this.program, "fadeOut");
this.refRatio = this.ctx.getUniformLocation(this.program, "ratio");
this.refSize = this.ctx.getUniformLocation(this.program, "size");
// Assig values to uniforms
this.ctx.uniform1f(this.refTimeFactor, this.timeFactor);
this.ctx.uniform1f(this.refLifetime, this.lifetime);
this.ctx.uniform1f(this.refFadeOut, this.fadeOut);
this.ctx.uniform1f(this.refRatio, this.ratio);
this.ctx.uniform1f(this.refSize, this.size);
// Uniform des textures
if(!this.texture)
return;
if(this.texture)
this.texture = document.querySelector(this.texture);
var texture = this.ctx.createTexture();
this.ctx.bindTexture(this.ctx.TEXTURE_2D, texture);
this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, this.texture);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST);
this.ctx.activeTexture(this.ctx.TEXTURE0);
this.refTexture = this.ctx.getUniformLocation(this.program, "texture");
this.ctx.uniform1i(this.textureLocation, 0);
}
initBlending(){
/*
--------------------------------------------
Paramètres de gestion des couleurs
--------------------------------------------
*/
this.ctx.enable(this.ctx.BLEND);
this.ctx.blendFunc(this.ctx.SRC_ALPHA,this.ctx.ONE_MINUS_SRC_ALPHA);
this.ctx.disable(this.ctx.DEPTH_TEST);
this.ctx.clearColor(0,0,0,0.0);
}
initHUD(){
document.querySelector('#nbr-particles span').innerHTML = this.quantity;
}
render() {
// Rendu du canvas webgl
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
let time = (Date.now() - this.startTime)/1000;
this.ctx.uniform1f(this.refTime, time);
this.ctx.drawArrays(this.ctx.POINTS, 0, this.quantity);
this.loopID = window.requestAnimationFrame(()=>{
this.render();
});
}
getShader(){
return `
precision mediump float;
attribute vec2 speed_angle;
attribute vec2 position;
attribute vec2 gravity;
attribute vec3 color;
uniform float time;
uniform float timeFactor;
uniform float ratio;
uniform float size;
varying lowp vec3 vColor;
void main(void) {
gl_PointSize = size;
vColor = color;
// float modTime = mod(time*10., 1.);
float modTime = time / timeFactor;
float speed = speed_angle.x;
float angle = speed_angle.y;
float G = gravity.x;
// if(sin((speed*(time))) > .5)
// gl_PointSize *= gl_PointSize * sin(speed);
float x0 = position.x;
float y0 = position.y;
// Equations horaires de la parabole
// ---------------------------------
float x = speed * cos(angle) * modTime + x0;
float y = -.5 * G * pow(modTime, 2.) + speed * sin(angle) * modTime + y0;
// Ratio canvas
// ---------------------------------
x /= ratio;
// x = x + sin(y)/.2;
gl_Position=vec4( x , y, 0.,1.);
}`;
}
getFragmentShader(){
return `
precision mediump float;
varying lowp vec3 vColor;
uniform float time;
uniform float lifetime;
uniform float fadeOut;
uniform sampler2D texture;
vec4 baseTexture;
void main(void) {
// float modTime = mod(T*10., 1.);
vec3 fragmentColor = vColor;
// float x = gl_PointCoord.x * sin(T*10.*V.x);
// float y = gl_PointCoord.y * sin(T*10.*V.y);
// vec2 coords = vec2(x,y);
// baseTexture = texture2D( texture, coords);
baseTexture = texture2D( texture, gl_PointCoord);
// fadeOut
// ----------------------------------
float opacity = 1.;
if(time > lifetime)
opacity -= (time - lifetime) / fadeOut;
// To the white
// ----------------------------------
float whiteDuration = .5;
if(time < whiteDuration){
float deltaColorR = (1. - fragmentColor.x) * (whiteDuration - time);
float deltaColorG = (1. - fragmentColor.y) * (whiteDuration - time);
float deltaColorB = (1. - fragmentColor.z) * (whiteDuration - time);
fragmentColor = vec3(fragmentColor.x + deltaColorR, fragmentColor.y + deltaColorG, fragmentColor.z + deltaColorB);
}
gl_FragColor = vec4(fragmentColor.x, fragmentColor.y, fragmentColor.z, opacity);
// gl_FragColor = vec4(vColor.x, vColor.y, vColor.z, opacity);
// gl_FragColor = vec4(.6, 1.0, 1.0, opacity);
// gl_FragColor = vec4(1.0, 1.0, 1., 1.);
// gl_FragColor = baseTexture * vec4(.7 * sin(V.x), .0, 1., 1.);
// gl_FragColor = baseTexture;
// rotation
// https://www.py4u.net/discuss/95325
}`;
}
autokill(){
setTimeout(()=>{
this.kill();
// myParticle();
}, (this.lifetime + this.fadeOut)*1000);
}
kill(){
this.ctx.bindTexture(this.ctx.TEXTURE_2D, null);
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, null);
this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, null);
this.ctx.bindRenderbuffer(this.ctx.RENDERBUFFER, null);
this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, null);
this.ctx.deleteBuffer(this.bufferSpeedAngle);
this.ctx.deleteBuffer(this.bufferPosition);
this.ctx.deleteBuffer(this.bufferGravity);
this.ctx.deleteProgram(this.program);
this.ctx.deleteShader(this.fragmentShader);
this.ctx.deleteShader(this.vertexShader);
//
window.cancelAnimationFrame(this.loopID);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
// console.clear();
// new Particle(1000);
}
} // End of class
您(不必要地)创建了两个着色器程序,每个实例化一个,但您只在 initProgram
方法中调用 useProgram
,但是调用 useProgram
会设置一个 global 呈现上下文的状态(在两种情况下都与从同一个 canvas 请求的相同)导致程序保持绑定状态直到更改。因此,当您使用第一个发射器进行渲染时,它会尝试使用第二个发射器(一旦创建)的着色器程序进行渲染。每次渲染时,您都需要 select 要渲染的程序,因此请在 render
方法中调用 useProgram
。