Я не знаю, что вы ожидали, и вы, очевидно, уже отредактировали свой кодовый блок, сделав ваш вопрос совершенно неуместным, поскольку мы больше не можем проверять проблему.В следующий раз используйте фрагмент
С preserveDrawingBuffer: false
холст очищается каждый кадр.в clearCanvas
вы очищаете альфа до единицы, а затем выключаете рендеринг в альфа, но поскольку preserveDrawingBuffer
имеет значение false (по умолчанию), буфер рисования очищается, что означает, что альфа теперь вернулась к нулю.После этого вы рендерите в него 0,0,0 или 1,1,0.1,1,0,0 - недопустимый цвет, если premultipliedAlpha
- это значение по умолчанию.Зачем?Потому что premultiplied
означает, что цвета, которые вы положили на холст, были умножены на альфа.альфа равен 0. 0 раз все равно нулю, поэтому когда альфа равна нулю, красный, зеленый и синий также должны быть равны нулю.
Вот почему вы видите разные цвета в разных браузерах.Когда ваши цвета недопустимы, результаты не определены.
Установка preserveDrawingBuffer
в значение true не решает вашу проблему.Это просто означает, что вы устанавливаете альфа на 1, а затем оставляете его равным 1, так как вы отключили рендеринг на альфа, чтобы весь холст стал непрозрачным.не совсем ясно (пусть preserverDrawingBuffer: false, сделайте очистку за вас) и не выключайте рендеринг в альфа с gl.colorMask
, затем в вашем шейдере напишите 0 в альфа, где вы хотите видеть фон, и 1, где вы не делаетет
const vertexSource = `
attribute vec3 pos;
void main() {
gl_Position=vec4(pos, .5);
}
`;
const fragmentSource = `
precision mediump float;
uniform float time;
uniform vec2 resolution;
mat2 rotate2d(float angle){
return mat2(cos(angle),-sin(angle),
sin(angle),cos(angle));
}
float variation(vec2 v1, vec2 v2, float strength, float speed) {
return sin(
dot(normalize(v1), normalize(v2)) * strength + time * speed
) / 100.0;
}
// vec3 paintCircle (vec2 uv, vec2 center, float rad, float width) {
vec4 paintCircle (vec2 uv, vec2 center, float rad, float width) {
vec2 diff = center-uv;
float len = length(diff);
len += variation(diff, vec2(0.0, 1.0), 3.0, 2.0);
len -= variation(diff, vec2(1.0, 0.0), 3.0, 2.0);
float circle = 1. -smoothstep(rad-width, rad, len);
// return vec3(circle);
return vec4(circle);
}
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
// vec3 color;
vec4 color;
float radius = 0.15;
vec2 center = vec2(0.5);
color = paintCircle(uv, center, radius, .2);
vec2 v = rotate2d(time) * uv;
//color *= vec3(v.x, v.y, 0.7-v.y*v.x);
// color *= vec3(255,255, 0);
color *= vec4(255,255, 0,255);
//color += paintCircle(uv, center, radius, 0.01);
// gl_FragColor = vec4(color, 1.0);
gl_FragColor = color;
}
`;
class Render {
constructor() {
this.pos = [];
this.program = [];
this.buffer = [];
this.ut = [];
this.resolution = [];
this.frame = 0;
this.start = Date.now();
this.options = {
// these are already the defaults
// alpha: true,
// premultipliedAlpha: true,
// preserveDrawingBuffer: false
};
this.canvas = document.querySelector('canvas');
this.gl = this.canvas.getContext('webgl', this.options);
this.width = this.canvas.width;
this.height = this.canvas.height;
this.gl.viewport(0, 0, this.width, this.height);
// this.gl.enable(this.gl.BLEND);
// this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
//this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
// this.clearCanvas();
window.addEventListener('resize', this.resetCanvas, true);
this.init();
}
init = () => {
this.createGraphics(vertexSource, fragmentSource, 0);
this.renderLoop();
};
resetCanvas = () => {
this.width = 300; //this.shaderCanvas.width;
this.height = 300; // this.shaderCanvas.height;
this.gl.viewport(0, 0, this.width, this.height);
this.clearCanvas();
};
createShader = (type, source) => {
let shader = this.gl.createShader(type);
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
let success = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS);
if (!success) {
console.log(this.gl.getShaderInfoLog(shader));
this.gl.deleteShader(shader);
return false;
}
return shader;
};
createProgram = (vertexSource, fragmentSource) => {
// Setup Vertext/Fragment Shader functions //
this.vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
this.fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentSource);
// Setup Program and Attach Shader functions //
let program = this.gl.createProgram();
this.gl.attachShader(program, this.vertexShader);
this.gl.attachShader(program, this.fragmentShader);
this.gl.linkProgram(program);
this.gl.useProgram(program);
return program;
};
createGraphics = (vertexSource, fragmentSource, i) => {
// Create the Program //
this.program[i] = this.createProgram(vertexSource, fragmentSource);
// Create and Bind buffer //
this.buffer[i] = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer[i]);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array([-1, 1, -1, -1, 1, -1, 1, 1]),
this.gl.STATIC_DRAW
);
this.pos[i] = this.gl.getAttribLocation(this.program[i], 'pos');
this.gl.vertexAttribPointer(
this.pos[i],
2, // size: 2 components per iteration
this.gl.FLOAT, // type: the data is 32bit floats
false, // normalize: don't normalize the data
0, // stride: 0 = move forward size * sizeof(type) each iteration to get the next position
0 // start at the beginning of the buffer
);
this.gl.enableVertexAttribArray(this.pos[i]);
this.importProgram(i);
};
updateUniforms = (i) => {
this.importUniforms(i);
this.gl.drawArrays(
this.gl.TRIANGLE_FAN, // primitiveType
0, // Offset
4 // Count
);
};
importProgram = (i) => {
this.ut[i] = this.gl.getUniformLocation(this.program[i], 'time');
this.resolution[i] = new Float32Array([300, 300]);
this.gl.uniform2fv(
this.gl.getUniformLocation(this.program[i],'resolution'),
this.resolution[i]
);
};
importUniforms = (i) => {
this.gl.uniform1f(this.ut[i], (Date.now() - this.start) / 1000);
};
renderLoop = () => {
this.frame++;
this.updateUniforms(0);
this.animation = window.requestAnimationFrame(this.renderLoop);
};
}
let demo = new Render(document.body);
body {
background-color: red;
background-image: linear-gradient(45deg, blue 25%, transparent 25%, transparent 75%, blue 75%, blue),
linear-gradient(-45deg, blue 25%, transparent 25%, transparent 75%, blue 75%, blue);
background-size: 30px 30px;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
width: 300px;
height: 300px;
background: transparent;
}
<canvas width="300" height="300"></canvas>
Примечание. Я установил фон на шаблон, чтобы мы могли видеть, как он работает.
Не уверен, что вы имели в виду эту строку
color *= vec4(255,255, 0,255);
для использования 255. Цвета в WebGL изменяются от 0 до 1, поэтому, возможно, вы действительно имели в виду
color *= vec4(1, 1, 0, 1);
Позвольте мне добавить, что есть также некоторые незначительные проблемы с кодом.Многие из них являются мнениями, поэтому возьмите их или оставьте их.
CSS
Самый простой способ получить холст для заполнения экрана - это
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
Это все, что вам нужно
Изменение размера при изменении размера события
Я бы сказал, Есть лучшие способы
Использование Date.now
requestAnimationFrame проходит за время с момента загрузки страницы до обратного вызова и имеет более высокое разрешение, чем Date.now()
Структура кода
Конечно, я не знаю ваших планов, но ожидать, что каждая пара шейдеров будет использовать одни и те же входные данные, кажется необычным.Конечно, это ваш код, так что, возможно, это то, что вы хотели.
Код настроен для нескольких программ, но вызывает gl.useProgram
один раз.
Похоже, updateUniforms
следуетВы вызываете gl.useProgram
, так что это влияет на правильную программу?
Использование функций стрелок в методах класса?
См. https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1
Также AFAIK thisформат пока не поддерживается только в Firefox или Safari Chrome (хотя вы можете использовать Babel для перевода)
Не настраивать область просмотра для каждого кадра
Это очень личное мнение, носкорее всего, в какой-то момент вы добавите фреймбуферы разных размеров, и в этот момент вам нужно будет постоянно устанавливать область просмотра.Микрооптимизация того, что происходит, когда фрейм едва ли стоит того.
Передача position
в качестве атрибута vec3
по умолчанию 0,0,0,1так что если вы не получите все 4 значения из ваших буферов, вы получите именно то, что вам нужно.
Вот версия с некоторыми из этих изменений
const vertexSource = `
attribute vec4 pos;
void main() {
gl_Position = pos;
}
`;
const fragmentSource = `
precision mediump float;
uniform float time;
uniform vec2 resolution;
mat2 rotate2d(float angle){
return mat2(cos(angle),-sin(angle),
sin(angle),cos(angle));
}
float variation(vec2 v1, vec2 v2, float strength, float speed) {
return sin(
dot(normalize(v1), normalize(v2)) * strength + time * speed
) / 100.0;
}
vec4 paintCircle (vec2 uv, vec2 center, float rad, float width) {
vec2 diff = center-uv;
float len = length(diff);
len += variation(diff, vec2(0.0, 1.0), 3.0, 2.0);
len -= variation(diff, vec2(1.0, 0.0), 3.0, 2.0);
float circle = 1. -smoothstep(rad-width, rad, len);
return vec4(circle);
}
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 color;
float radius = 0.15;
vec2 center = vec2(0.5);
color = paintCircle(uv, center, radius, .2);
vec2 v = rotate2d(time) * uv;
color *= vec4(1,1, 0,1);
gl_FragColor = color;
}
`;
class Render {
constructor() {
this.pos = [];
this.program = [];
this.buffer = [];
this.ut = [];
this.ures = [];
this.frame = 0;
this.canvas = document.querySelector('canvas');
this.gl = this.canvas.getContext('webgl');
this.renderLoop = this.renderLoop.bind(this);
this.init();
}
init() {
this.createGraphics(vertexSource, fragmentSource, 0);
this.renderLoop(0);
}
createShader(type, source) {
let shader = this.gl.createShader(type);
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
let success = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS);
if (!success) {
console.log(this.gl.getShaderInfoLog(shader));
this.gl.deleteShader(shader);
return false;
}
return shader;
}
createProgram (vertexSource, fragmentSource) {
// Setup Vertext/Fragment Shader functions //
this.vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
this.fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentSource);
// Setup Program and Attach Shader functions //
let program = this.gl.createProgram();
this.gl.attachShader(program, this.vertexShader);
this.gl.attachShader(program, this.fragmentShader);
this.gl.linkProgram(program);
return program;
}
createGraphics (vertexSource, fragmentSource, i) {
// Create the Program //
this.program[i] = this.createProgram(vertexSource, fragmentSource);
// Create and Bind buffer //
this.buffer[i] = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer[i]);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array([-1, 1, -1, -1, 1, -1, 1, 1]),
this.gl.STATIC_DRAW
);
this.pos[i] = this.gl.getAttribLocation(this.program[i], 'pos');
this.gl.vertexAttribPointer(
this.pos[i],
2, // size: 2 components per iteration
this.gl.FLOAT, // type: the data is 32bit floats
false, // normalize: don't normalize the data
0, // stride: 0 = move forward size * sizeof(type) each iteration to get the next position
0 // start at the beginning of the buffer
);
this.gl.enableVertexAttribArray(this.pos[i]);
this.importProgram(i);
}
updateUniforms(i, time) {
this.gl.useProgram(this.program[i]);
this.importUniforms(i, time);
this.gl.drawArrays(
this.gl.TRIANGLE_FAN, // primitiveType
0, // Offset
4 // Count
);
};
importProgram(i) {
this.ut[i] = this.gl.getUniformLocation(this.program[i], 'time');
this.ures[i] = this.gl.getUniformLocation(this.program[i],'resolution');
};
importUniforms(i, time) {
this.gl.uniform1f(this.ut[i], time / 1000);
this.gl.uniform2f(this.ures[i], this.gl.canvas.width, this.gl.canvas.height);
}
resizeCanvasToDisplaySize() {
const canvas = this.gl.canvas;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width ||
canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
renderLoop(time) {
this.resizeCanvasToDisplaySize();
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
this.frame++;
this.updateUniforms(0, time);
this.animation = window.requestAnimationFrame(this.renderLoop);
}
}
let demo = new Render(document.body);
body {
background-color: red;
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<canvas></canvas>