webgl обработка изображений, среднее между средним, правым и левым пикселем
Изучаю как обрабатывать изображения в webgl
Не понимаю что это значит
// среднее между средним, правым и левым пикселем
gl_FragColor = (
texture2D(u_image, v_texCoord) +
texture2D(u_image, v_texCoord + vec2(onePixel.x, 0.0)) +
texture2D(u_image, v_texCoord + vec2(-onePixel.x, 0.0))) / 3.0;
И вообще не понимаю как это выполняется, объясните по простому
function debounce(f, t) {
return function(args) {
let previousCall = this.lastCall;
this.lastCall = Date.now();
if (previousCall && ((this.lastCall - previousCall) <= t)) {
clearTimeout(this.lastCallTimer);
}
this.lastCallTimer = setTimeout(() => f(args), t);
}
}
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const sucess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (sucess) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, v, f) {
const program = gl.createProgram();
gl.attachShader(program, v);
gl.attachShader(program, f);
gl.linkProgram(program);
const sucess = gl.getProgramParameter(program, gl.LINK_STATUS);
if (sucess) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function resizeCanvas(gl) {
const realToCSSPixels = window.devicePixelRatio; // для дисплеев повышенной четкости HD-DPI
const width = Math.floor(gl.canvas.scrollWidth * realToCSSPixels);
const height = Math.floor(gl.canvas.scrollHeight * realToCSSPixels);
gl.canvas.width = width;
gl.canvas.height = height;
gl.viewport(0, 0, width, height);
}
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
function drawWebglCanvas(f, v, gl, image) {
function resizeGlAndCanvas(gl) {
resizeCanvas(gl);
const resizeFunc = () => {
resizeCanvas(gl);
render();
};
const lazyFunc = debounce(resizeFunc, 100);
window.addEventListener('resize', lazyFunc);
}
resizeGlAndCanvas(gl);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
const vertexShader = createShader(gl, gl.VERTEX_SHADER, v);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, f);
const program = createProgram(gl, vertexShader, fragmentShader);
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");
gl.useProgram(program);
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
const positionBuffer = gl.createBuffer();
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
setRectangle(gl, 0, 0, gl.canvas.width, gl.canvas.height);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
// указываем координаты текстуры для прямоугольника
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// создаём текстуру
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// задаём параметры, чтобы можно было отрисовать изображение любого размера
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
const textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
gl.uniform2f(textureSizeLocation, gl.canvas.width, gl.canvas.height);
function render() {
const primitiveType = gl.TRIANGLES;
const offset = 0;
const count = 6;
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(primitiveType, offset, count);
}
render();
}
function setRectangle(gl, x, y, width, height) {
const x1 = x;
const x2 = x + width;
const y1 = y;
const y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]), gl.STATIC_DRAW);
}
const fragment = document.querySelector('#fragment-shader-2d').innerHTML;
const vertex = document.querySelector('#vertex-shader-2d').innerHTML;
const canvas = document.querySelector('#glcanvas');
const gl = canvas.getContext("webgl");
const image = new Image();
const url = "https://picsum.photos/1000/700";
requestCORSIfNotSameOrigin(image, url)
image.src = url;
image.crossOrigin = "";
image.addEventListener('load', () => {
drawWebglCanvas(fragment, vertex, gl, image);
});
*,
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
}
body,
html {
position: relative;
height: 100%;
}
#glcanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
<script id="vertex-shader-2d" type="x-shader/x-vertex">
attribute vec2 a_texCoord;
attribute vec2 a_position;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// преобразуем положение в пикселях к диапазону от 0.0 до 1.0
vec2 zeroToOne = a_position / u_resolution;
// преобразуем из 0->1 в 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// преобразуем из 0->2 в -1->+1 (пространство отсечения)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
v_texCoord = a_texCoord;
}
</script>
<script id="fragment-shader-2d" type="not-js">
precision mediump float;
uniform sampler2D u_image; // наша текстура
uniform vec2 u_textureSize; // размер текстуры
varying vec2 v_texCoord; // texCoords, переданные из вершинного шейдера
void main(){
// рассчитываем один пиксель в текстурных координатах
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
// среднее между средним, правым и левым пикселем
gl_FragColor = (
texture2D(u_image, v_texCoord) +
texture2D(u_image, v_texCoord + vec2(onePixel.x, 0.0)) +
texture2D(u_image, v_texCoord + vec2(-onePixel.x, 0.0))) / 3.0;
}
</script>
<canvas id="glcanvas">
Ответы (1 шт):
Автор решения: Михаил Камахин
→ Ссылка
gl_FragColor принимает vec4
texture2D возвращает vec4 по координатам vec2 в текстуре
Это можно легче понять, если просто сделать переменные среднего, левого и правого пикселя
void main(){
// рассчитываем один пиксель в текстурных координатах
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
vec2 mediumPixel = v_texCoord;
vec2 rightPixel = v_texCoord + vec2(onePixel.x, 0.0);
vec2 leftPixel = v_texCoord + vec2(-onePixel.x, 0.0);
// среднее между средним, правым и левым пикселем
gl_FragColor = (
texture2D(u_image, mediumPixel) +
texture2D(u_image, rightPixel) +
texture2D(u_image, leftPixel)
) / 3.0;
}
В итоге мы складываем три vec4, делим на 3, получаем средний цвет между этими тремя пикселями