Skip to content

Instantly share code, notes, and snippets.

@asc0910
Created April 1, 2023 08:55
Show Gist options
  • Save asc0910/bde4e01e364e2de7bd3badd940b8ff12 to your computer and use it in GitHub Desktop.
Save asc0910/bde4e01e364e2de7bd3badd940b8ff12 to your computer and use it in GitHub Desktop.
mapping
@import url("https://webglfundamentals.org/webgl/resources/webgl-tutorials.css");
body {
margin: 0;
}
#canvas {
width: 3000px;
height: 3000px;
display: block;
}
<canvas id="canvas"></canvas>
<!-- vertex shader -->
<script src="https://www.lactame.com/lib/image-js/0.21.2/image.min.js"></script>
<script id="vertex-shader-2d" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision highp float;
// our textures
uniform sampler2D artwork;
uniform sampler2D uv;
uniform sampler2D base;
uniform sampler2D gloss;
uniform sampler2D mask;
uniform sampler2D background;
uniform float width;
uniform float height;
uniform float glossFactor;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
vec4 getColor(vec2 p, float dx, float dy) {
vec2 coord = vec2(p.x + dx / width, p.y + dy / height);
vec4 color1 = texture2D(uv, coord);
float u = (floor(color1.r * 255.0) * 256.0 + floor(color1.g * 255.0)) / 65536.0;
float v = (floor(color1.b * 255.0) * 256.0 + floor(color1.a * 255.0)) / 65536.0;
float eps = 0.00001;
vec4 result;
if (abs(u) > eps && abs(v) > eps)
return texture2D(artwork, vec2(u, v));
else
return vec4(0, 0, 0, -1);
}
highp vec4 blend(vec4 a, vec4 b) {
if (a.a <= 0.0 && b.a <= 0.0) {
return (a * a.a + b * (1.0 - a.a) * b.a);
}
float ta = a.a + b.a - a.a * b.a;
vec4 ret = (a * a.a + b * (1.0 - a.a) * b.a) / ta;
ret.a = ta;
return ret;
}
void main() {
vec2 coord = vec2(v_texCoord.x, v_texCoord.y);
vec4 result = getColor(coord, 0.0, 0.0);
if (result.a < 0.0) {
result = vec4(0, 0, 0, 0);
float cnt = 0.0;
for (float dx = -1.0; dx < 2.0; dx += 1.0) {
for (float dy = -1.0; dy < 2.0; dy += 1.0) {
vec4 color = getColor(coord, dx, dy);
if (color.a > 0.0) {
cnt += 1.0;
result += color;
}
}
}
if (cnt > 0.0) {
result = result / cnt;
}
}
vec4 glossu = texture2D(gloss, coord);
float masku = texture2D(mask, coord).a;
if (masku <= 0.5) {
float cnt = 0.0;
float sum = 0.0;
for (float dx = -2.0; dx < 3.0; dx += 1.0) {
for (float dy = -2.0; dy < 3.0; dy += 1.0) {
vec4 d = texture2D(mask, vec2(v_texCoord.x + dx / width, v_texCoord.y + dy / height));
if (d.a > 0.0) {
cnt += 1.0;
sum += 1.0;
}
}
}
if (cnt >= 4.0) {
masku = 1.0;
} else if (cnt > 0.0) {
masku = sum / cnt;
}
}
result = result * masku;
vec4 baseu = texture2D(base, coord);
float glossv = glossu.r;
float glossa = glossu.a;
result = result * baseu;
if (glossa >= 0.0) {
float a = result.a;
result = result + vec4(glossv, glossv, glossv, 1) - result * glossv;
result.a = a;
}
result = blend(result, baseu);
gl_FragColor = result;
}
</script>
<script id="fragment-shader-2d-blur" type="x-shader/x-fragment">
precision highp float;
// our textures
uniform sampler2D inputa;
uniform sampler2D zdepth;
uniform float width;
uniform float height;
uniform float focus;
uniform float strength;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
vec4 getColor(vec2 p, float dx, float dy) {
vec2 coord = vec2(p.x + dx / width, p.y + dy / height);
return texture2D(inputa, coord);
}
void main() {
vec4 result = getColor(v_texCoord, 0.0, 0.0);
float zdepthu = texture2D(zdepth, v_texCoord).r;
float Size = abs((focus - zdepthu)) * strength - 0.5;
float weight = 1.0;
if (Size > 0.0) {
float sigma = Size;
for (float dx = -10.0; dx <= 10.0; dx += 1.0) {
for (float dy = -10.0; dy <= 10.0; dy += 1.0) {
float w = 1.0 / sqrt(6.28 * sigma * sigma) * exp(- (dx * dx + dy * dy) / (2.0 * sigma * sigma));
result += w * getColor(v_texCoord, dx, dy);
weight += w;
}
}
result /= weight;
}
gl_FragColor = result;
}
</script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://www.lactame.com/lib/image-js/0.21.2/image.min.js"></script>
"use strict";
const assets = {
artwork: 'https://res.cloudinary.com/casestry-com/image/upload/v1677097709/test/Custom_iwytor.png',
base: 'https://res.cloudinary.com/casestry-com/image/upload/v1680338078/test3/base_cfkxxf.png',
gloss: 'https://res.cloudinary.com/casestry-com/image/upload/v1680338078/test3/gloss_sv8a3d.png',
mask: 'https://res.cloudinary.com/casestry-com/image/upload/v1680338077/test3/mask_vedkjc.png',
uv: 'https://res.cloudinary.com/casestry-com/image/upload/v1680338078/test3/uv_qhioig.png',
zdepth: 'https://res.cloudinary.com/casestry-com/image/upload/v1677092667/test/zdepth_vnhyk4.png',
}
const focus = 1.0
const strength = 8;
async function loadImage(url) {
return new Promise((resolve) => {
var image = new Image();
requestCORSIfNotSameOrigin(image, url)
image.src = url;
image.onload = () => {
resolve(image)
};
})
}
async function loadImage64(url) {
const image = await IJS.Image.load(url);
const data = new Uint8Array(image.height * image.width * 4);
for (let i = 0; i < image.height * image.width; i += 1) {
data[i * 4] = Math.floor(image.data[i * 4] / 256);
data[i * 4 + 1] = image.data[i * 4] % 256;
data[i * 4 + 2] = Math.floor(image.data[i * 4 + 1] / 256);
data[i * 4 + 3] = image.data[i * 4 + 1] % 256;
}
const newImage = new IJS.Image(image.width, image.height, data, { kind: 'RGBA' });
return newImage.getCanvas();
}
async function loadImage64_1(url) {
const image = await IJS.Image.load(url);
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(image.width, image.height);
for (let i = 0; i < image.height * image.width; i += 1) {
imgData.data[i * 4] = Math.floor(image.data[i * image.channels] / 256);
imgData.data[i * 4 + 1] = image.data[i * image.channels] % 256;
imgData.data[i * 4 + 2] = Math.floor(image.data[i * image.channels + 1] / 256);
imgData.data[i * 4 + 3] = image.data[i * image.channels + 1] % 256;
}
return imgData;
}
const a5Width = 1748;
const a5Height = 2480;
async function main() {
const artwork = await loadImage(assets.artwork)
const base = await loadImage(assets.base);
const uv = await loadImage64_1(assets.uv);
const gloss = await loadImage(assets.gloss);
const mask = await loadImage(assets.mask);
const zdepth = await loadImage(assets.zdepth);
const canvas = render([artwork, base, uv, gloss, mask])
// blur([canvas, zdepth])
}
var documentCanvas = document.querySelector('#canvas');
function blur(images) {
var canvas = documentCanvas;
canvas.width = images[1].width;
canvas.height = images[1].height;
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d-blur"]);
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
// Create a buffer to put three 2d clip space points in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// provide texture coordinates for the rectangle.
var 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);
// create 2 textures
var textures = [];
for (var ii = 0; ii < 2; ++ii) {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
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.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[ii]);
// add the texture to the array of textures.
textures.push(texture);
}
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// lookup the sampler locations.
var inputLocation = gl.getUniformLocation(program, "inputa");
var zdepthLocation = gl.getUniformLocation(program, "zdepth");
var widthLocation = gl.getUniformLocation(program, "width");
var heightLocation = gl.getUniformLocation(program, "height");
var focusLocation = gl.getUniformLocation(program, "focus");
var strengthLocation = gl.getUniformLocation(program, "strength");
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the position attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// Turn on the texcoord attribute
gl.enableVertexAttribArray(texcoordLocation);
// bind the texcoord buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// Tell the texcoord attribute how to get data out of texcoordBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset);
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// set which texture units to render with.
gl.uniform1i(inputLocation, 0); // texture unit 0
gl.uniform1i(zdepthLocation, 1); // texture unit 1
gl.uniform1f(widthLocation, gl.canvas.width);
gl.uniform1f(heightLocation, gl.canvas.height);
gl.uniform1f(focusLocation, focus);
gl.uniform1f(strengthLocation, strength);
// Set each texture unit to use a particular texture.
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[1]);
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
return canvas;
}
function render(images) {
var starttime = Date.now();
// Get A WebGL context
/** @type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas");
canvas.width = images[1].width;
canvas.height = images[1].height;
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
// Create a buffer to put three 2d clip space points in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// provide texture coordinates for the rectangle.
var 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);
// create 2 textures
var textures = [];
for (var ii = 0; ii < 5; ++ii) {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
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.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[ii]);
// add the texture to the array of textures.
textures.push(texture);
}
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// lookup the sampler locations.
var artworkLocation = gl.getUniformLocation(program, "artwork");
var baseLocation = gl.getUniformLocation(program, "base");
var uvLocation = gl.getUniformLocation(program, "uv");
var glossLocation = gl.getUniformLocation(program, "gloss");
var maskLocation = gl.getUniformLocation(program, "mask");
var widthLocation = gl.getUniformLocation(program, "width");
var heightLocation = gl.getUniformLocation(program, "height");
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the position attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// Turn on the texcoord attribute
gl.enableVertexAttribArray(texcoordLocation);
// bind the texcoord buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// Tell the texcoord attribute how to get data out of texcoordBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset);
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// set which texture units to render with.
gl.uniform1i(artworkLocation, 0); // texture unit 0
gl.uniform1i(baseLocation, 1); // texture unit 1
gl.uniform1i(uvLocation, 2); // texture unit 2
gl.uniform1i(glossLocation, 3); // texture unit 3
gl.uniform1i(maskLocation, 4); // texture unit 4
gl.uniform1f(widthLocation, gl.canvas.width); // texture unit 3
gl.uniform1f(heightLocation, gl.canvas.height); // texture unit 4
// Set each texture unit to use a particular texture.
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[1]);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, textures[2]);
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, textures[3]);
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
console.log('Elapsed: ', Date.now() - starttime);
return canvas;
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var 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);
}
main();
// This is needed if the images are not on the same domain
// NOTE: The server providing the images must give CORS permissions
// in order to be able to use the image with WebGL. Most sites
// do NOT give permission.
// See: https://webglfundamentals.org/webgl/lessons/webgl-cors-permission.html
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url, window.location.href)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
{"name":"mapping","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment