Browse Source

blending in linear colour space before converting to sRGB space

master
Inderjit Gill 4 months ago
parent
commit
73483b1b31

+ 1
- 6
cli/src/main.rs View File

@@ -139,12 +139,7 @@ fn load_bitmap(asset_prefix: &String, filename: &String, context: &mut Context)
data.push(rgba.data[3]);
}

let bitmap_info = BitmapInfo {
width,
height,
data,
..Default::default()
};
let bitmap_info = BitmapInfo::new(width, height, data);

context.bitmap_cache.insert(&filename, bitmap_info)?;


+ 1
- 6
client/src/lib.rs View File

@@ -192,12 +192,7 @@ impl Bridge {
}

pub fn add_rgba_bitmap(&mut self, name: &str, width: usize, height: usize, data: Vec<u8>) {
let bitmap_info = BitmapInfo {
width,
height,
data,
..Default::default()
};
let bitmap_info = BitmapInfo::new(width, height, data);

self.context.bitmap_cache.insert(name, bitmap_info).unwrap();
}

+ 332
- 111
client/www/index.js View File

@@ -109,19 +109,21 @@ function compileShader(gl, type, src) {

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
//alert(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}

function setupShaders(gl) {
const shaderProgram = gl.createProgram();
function setupPieceShaders(gl) {
const shader = {};

shader.program = gl.createProgram();

// pre-multiply the alpha in the shader
// see http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
const fragmentSrc = `
precision mediump float;
precision highp float;
varying vec4 vColor;
varying highp vec2 vTextureCoord;

@@ -134,7 +136,6 @@ function setupShaders(gl) {
gl_FragColor.g = tex.r * vColor.g * vColor.a;
gl_FragColor.b = tex.r * vColor.b * vColor.a;
gl_FragColor.a = tex.r * vColor.a;

}
`;

@@ -159,36 +160,108 @@ function setupShaders(gl) {
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSrc);
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc);

gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.attachShader(shader.program, vertexShader);
gl.attachShader(shader.program, fragmentShader);

gl.linkProgram(shader.program);

if (!gl.getProgramParameter(shader.program, gl.LINK_STATUS)) {
let lastError = gl.getProgramInfoLog(shader.program);

alert(`Could not initialise shaders: ${lastError}`);;
gl.deleteProgram(shader.program);
return null;
}

shader.positionAttribute = gl.getAttribLocation(shader.program, 'aVertexPosition');
shader.colourAttribute = gl.getAttribLocation(shader.program, 'aVertexColor');
shader.textureAttribute = gl.getAttribLocation(shader.program, 'aVertexTexture');

shader.pMatrixUniform = gl.getUniformLocation(shader.program, 'uPMatrix');
shader.mvMatrixUniform = gl.getUniformLocation(shader.program, 'uMVMatrix');
shader.textureUniform = gl.getUniformLocation(shader.program, 'uSampler');

return shader;
}

function setupBlitShaders(gl) {
const shader = {};

gl.linkProgram(shaderProgram);
shader.program = gl.createProgram();

// commented out because of jshint
// if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
//alert('Could not initialise shaders');
// }
const fragmentSrc = `
precision highp float;
varying vec2 vTextureCoord;

gl.useProgram(shaderProgram);
uniform sampler2D uSampler;

shaderProgram.positionAttribute =
gl.getAttribLocation(shaderProgram, 'aVertexPosition');
gl.enableVertexAttribArray(shaderProgram.positionAttribute);
// https:en.wikipedia.org/wiki/SRGB
vec3 linear_to_srgb(vec3 linear) {
float a = 0.055;
float b = 0.0031308;
vec3 srgb_lo = 12.92 * linear;
vec3 srgb_hi = (1.0 + a) * pow(linear, vec3(1.0/2.4)) - vec3(a);
return vec3(
linear.r > b ? srgb_hi.r : srgb_lo.r,
linear.g > b ? srgb_hi.g : srgb_lo.g,
linear.b > b ? srgb_hi.b : srgb_lo.b);
}

shaderProgram.colourAttribute =
gl.getAttribLocation(shaderProgram, 'aVertexColor');
gl.enableVertexAttribArray(shaderProgram.colourAttribute);
// https:twitter.com/jimhejl/status/633777619998130176
vec3 ToneMapFilmic_Hejl2015(vec3 hdr, float whitePt) {
vec4 vh = vec4(hdr, whitePt);
vec4 va = 1.425 * vh + 0.05;
vec4 vf = (vh * va + 0.004) / (vh * (va + 0.55) + 0.0491) - 0.0821;
return vf.rgb / vf.www;
}

shaderProgram.textureAttribute =
gl.getAttribLocation(shaderProgram, 'aVertexTexture');
gl.enableVertexAttribArray(shaderProgram.textureAttribute);
void main()
{
vec4 col = texture2D( uSampler, vTextureCoord );
gl_FragColor = vec4(linear_to_srgb(col.rgb), 1.0);
}
`;

shaderProgram.pMatrixUniform =
gl.getUniformLocation(shaderProgram, 'uPMatrix');
shaderProgram.mvMatrixUniform =
gl.getUniformLocation(shaderProgram, 'uMVMatrix');
const vertexSrc = `
attribute vec2 aVertexPosition;
attribute vec2 aVertexTexture;

return shaderProgram;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying highp vec2 vTextureCoord;

void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 0.0, 1.0);
vTextureCoord = aVertexTexture;
}
`;

const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSrc);
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc);

gl.attachShader(shader.program, vertexShader);
gl.attachShader(shader.program, fragmentShader);

gl.linkProgram(shader.program);

if (!gl.getProgramParameter(shader.program, gl.LINK_STATUS)) {
let lastError = gl.getProgramInfoLog(shader.program);

alert(`Could not initialise shaders: ${lastError}`);;
gl.deleteProgram(shader.program);
return null;
}

shader.positionAttribute = gl.getAttribLocation(shader.program, 'aVertexPosition');
shader.textureAttribute = gl.getAttribLocation(shader.program, 'aVertexTexture');

shader.pMatrixUniform = gl.getUniformLocation(shader.program, 'uPMatrix');
shader.mvMatrixUniform = gl.getUniformLocation(shader.program, 'uMVMatrix');
shader.textureUniform = gl.getUniformLocation(shader.program, 'uSampler');

return shader;
}

function setupGLState(gl) {
@@ -205,7 +278,7 @@ function setupGLState(gl) {
}


function handleTextureLoaded(gl, image, texture, shaderProgram) {
function handleTextureLoaded(gl, image, texture) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
@@ -217,9 +290,70 @@ function handleTextureLoaded(gl, image, texture, shaderProgram) {

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0);
}

function createRenderTexture(gl, config) {
// create to render to
const targetTextureWidth = config.render_texture_width;
const targetTextureHeight = config.render_texture_height;

const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);

{
// define size and format of level 0
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
targetTextureWidth, targetTextureHeight, border,
format, type, data);

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
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);
}

return targetTexture;
}

function createFrameBuffer(gl, targetTexture) {
// Create and bind the framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

// attach the texture as the first color attachment
const attachmentPoint = gl.COLOR_ATTACHMENT0;
const level = 0;
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level);

return fb;
}

function checkFramebufferStatus(gl) {
let res = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
switch(res) {
case gl.FRAMEBUFFER_COMPLETE: console.log("gl.FRAMEBUFFER_COMPLETE"); break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: console.log("gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: console.log("gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: console.log("gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); break;
case gl.FRAMEBUFFER_UNSUPPORTED: console.log("gl.FRAMEBUFFER_UNSUPPORTED"); break;
case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: console.log("gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); break;
case gl.RENDERBUFFER_SAMPLES: console.log("gl.RENDERBUFFER_SAMPLE"); break;
}
}

const gConfig = {
render_texture_width: 1024,
render_texture_height: 1024,
};

class GLRenderer {
constructor(canvasElement) {
this.glDomElement = canvasElement;
@@ -228,7 +362,9 @@ class GLRenderer {
const gl = initGL(this.glDomElement);
this.gl = gl;

this.shaderProgram = setupShaders(gl);
this.pieceShader = setupPieceShaders(gl);
this.blitShader = setupBlitShaders(gl);

setupGLState(gl);

this.glVertexBuffer = gl.createBuffer();
@@ -237,7 +373,12 @@ class GLRenderer {

this.mvMatrix = Matrix.create();
this.pMatrix = Matrix.create();
Matrix.ortho(this.pMatrix, 0, 1000, 0, 1000, 10, -10);

this.renderTexture = createRenderTexture(gl, gConfig);
this.framebuffer = createFrameBuffer(gl, this.renderTexture);

// render to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

loadTexture(src) {
@@ -250,7 +391,7 @@ class GLRenderer {
const image = new Image();

image.addEventListener('load', () => {
handleTextureLoaded(that.gl, image, that.texture, that.shaderProgram);
handleTextureLoaded(that.gl, image, that.texture);
resolve();
});

@@ -262,59 +403,28 @@ class GLRenderer {
});
}

copyImageDataTo(elem) {
return new Promise((resolve, reject) => {
try {
this.glDomElement.toBlob(blob => {
elem.src = window.URL.createObjectURL(blob);
return resolve();
});
} catch (error) {
return reject(error);
}
});
}

localDownload(filename) {
this.glDomElement.toBlob(function(blob) {
renderGeometryToTexture(destTextureWidth, destTextureHeight, memoryF32, buffers, section) {
const gl = this.gl;

const url = window.URL.createObjectURL(blob);
let shader = this.pieceShader;

let element = document.createElement('a');
element.setAttribute('href', url);
// element.setAttribute('target', '_blank');
element.setAttribute('download', filename);
// render to texture attached to framebuffer

element.style.display = 'none';
document.body.appendChild(element);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
//gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.viewport(0, 0, destTextureWidth, destTextureHeight);

element.click();
gl.useProgram(shader.program);

document.body.removeChild(element);
gl.enableVertexAttribArray(shader.positionAttribute);
gl.enableVertexAttribArray(shader.colourAttribute);
gl.enableVertexAttribArray(shader.textureAttribute);

downloadDialogHide();
});
}

preDrawScene(destWidth, destHeight, section) {
const gl = this.gl;
const domElement = this.glDomElement;

if (domElement.width !== destWidth) {
//log('GL width from', domElement.width, 'to', destWidth);
domElement.width = destWidth;
}
if (this.glDomElement.height !== destHeight) {
//log('GL height from', domElement.height, 'to', destHeight);
domElement.height = destHeight;
}
// gl.drawingBufferWidth, gl.drawingBufferHeight hold the actual
// size of the rendering element

gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
// gl.clearColor(0.0, 0.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


if (section === undefined) {
// render the entirety of the scene
Matrix.ortho(this.pMatrix, 0, 1000, 0, 1000, 10, -10);
@@ -331,18 +441,16 @@ class GLRenderer {
}
}

gl.uniformMatrix4fv(this.shaderProgram.pMatrixUniform,
gl.uniformMatrix4fv(shader.pMatrixUniform,
false,
this.pMatrix);

gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform,
gl.uniformMatrix4fv(shader.mvMatrixUniform,
false,
this.mvMatrix);
}

drawBuffer(memory, buffer) {
const gl = this.gl;
const shaderProgram = this.shaderProgram;
gl.uniform1i(shader.textureUniform, 0);


const glVertexBuffer = this.glVertexBuffer;
const glColourBuffer = this.glColourBuffer;
@@ -353,37 +461,156 @@ class GLRenderer {
const vertexItemSize = 2;
const colourItemSize = 4;
const textureItemSize = 2;

const totalSize = (vertexItemSize + colourItemSize + textureItemSize);

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#Syntax
// a new typed array view is created that views the specified ArrayBuffer
const gbuf = new Float32Array(memory, buffer.geo_ptr, buffer.geo_len);

buffers.forEach(buffer => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#Syntax
// a new typed array view is created that views the specified ArrayBuffer
const gbuf = new Float32Array(memoryF32, buffer.geo_ptr, buffer.geo_len);

//const gbuf = memorySubArray(memoryF32, geo_ptr, geo_len);

gl.bindBuffer(gl.ARRAY_BUFFER, glVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.positionAttribute,
vertexItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
0);

gl.bindBuffer(gl.ARRAY_BUFFER, glColourBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.colourAttribute,
colourItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
vertexItemSize * bytesin32bit);

gl.bindBuffer(gl.ARRAY_BUFFER, glTextureBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.textureAttribute,
textureItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
(vertexItemSize + colourItemSize) * bytesin32bit);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, buffer.geo_len / totalSize);

});

}

renderTextureToScreen(canvasWidth, canvasHeight) {
const gl = this.gl;
const domElement = this.glDomElement;


if (domElement.width !== canvasWidth) {
domElement.width = canvasWidth;
}
if (domElement.height !== canvasHeight) {
domElement.height = canvasHeight;
}

let shader = this.blitShader;

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, this.renderTexture);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

gl.useProgram(shader.program);

// console.log(shader);
gl.enableVertexAttribArray(shader.positionAttribute);
gl.enableVertexAttribArray(shader.textureAttribute);

// gl.clearColor(0.0, 0.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// render the entirety of the scene
Matrix.ortho(this.pMatrix, 0, canvasWidth, 0, canvasHeight, 10, -10);

// add some uniforms for canvas width and height

gl.uniformMatrix4fv(shader.pMatrixUniform,
false,
this.pMatrix);

gl.uniformMatrix4fv(shader.mvMatrixUniform,
false,
this.mvMatrix);

gl.uniform1i(shader.textureUniform, 0);

const glVertexBuffer = this.glVertexBuffer;
const glColourBuffer = this.glColourBuffer;
const glTextureBuffer = this.glTextureBuffer;

// x, y, u, v
const jsData = [
0.0, 0.0, 0.0, 0.0,
canvasWidth, 0.0, 1.0, 0.0,
0.0, canvasHeight, 0.0, 1.0,
canvasWidth, canvasHeight, 1.0, 1.0
];
const data = Float32Array.from(jsData);


const bytesin32bit = 4;

const vertexItemSize = 2;
const textureItemSize = 2;
const totalSize = (vertexItemSize + textureItemSize);

gl.bindBuffer(gl.ARRAY_BUFFER, glVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shaderProgram.positionAttribute,
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.positionAttribute,
vertexItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
0 * bytesin32bit);

gl.bindBuffer(gl.ARRAY_BUFFER, glColourBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shaderProgram.colourAttribute,
colourItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
vertexItemSize * bytesin32bit);
gl.FLOAT, false, totalSize * 4,
0);

gl.bindBuffer(gl.ARRAY_BUFFER, glTextureBuffer);
gl.bufferData(gl.ARRAY_BUFFER, gbuf, gl.STATIC_DRAW);
gl.vertexAttribPointer(shaderProgram.textureAttribute,
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.vertexAttribPointer(shader.textureAttribute,
textureItemSize,
gl.FLOAT, false, totalSize * bytesin32bit,
(vertexItemSize + colourItemSize) * bytesin32bit);
gl.FLOAT, false, totalSize * 4,
(vertexItemSize) * 4);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, jsData.length / totalSize);
}

copyImageDataTo(elem) {
return new Promise((resolve, reject) => {
try {
this.glDomElement.toBlob(blob => {
elem.src = window.URL.createObjectURL(blob);
return resolve();
});
} catch (error) {
return reject(error);
}
});
}

gl.drawArrays(gl.TRIANGLE_STRIP, 0, buffer.geo_len / totalSize);
localDownload(filename) {
this.glDomElement.toBlob(function(blob) {

const url = window.URL.createObjectURL(blob);

let element = document.createElement('a');
element.setAttribute('href', url);
// element.setAttribute('target', '_blank');
element.setAttribute('download', filename);

element.style.display = 'none';
document.body.appendChild(element);

element.click();

document.body.removeChild(element);

downloadDialogHide();
});
}

}

// --------------------------------------------------------------------------------
@@ -1447,11 +1674,8 @@ async function renderGeometryBuffers(memory, buffers, imageElement, w, h) {

const stopFn = startTiming();

gGLRenderer.preDrawScene(destWidth, destHeight);

buffers.forEach(buffer => {
gGLRenderer.drawBuffer(memory, buffer);
});
gGLRenderer.renderGeometryToTexture(gConfig.render_texture_width, gConfig.render_texture_height, memory, buffers);
gGLRenderer.renderTextureToScreen(destWidth, destHeight);

await gGLRenderer.copyImageDataTo(imageElement);

@@ -1471,11 +1695,8 @@ async function renderGeometryBuffersSection(memory, buffers, imageElement, w, h,

const stopFn = startTiming();

gGLRenderer.preDrawScene(destWidth, destHeight, section);

buffers.forEach(buffer => {
gGLRenderer.drawBuffer(memory, buffer);
});
gGLRenderer.renderGeometryToTexture(gConfig.render_texture_width, gConfig.render_texture_height, memory, buffers, section);
gGLRenderer.renderTextureToScreen(destWidth, destHeight);

await gGLRenderer.copyImageDataTo(imageElement);


+ 8
- 8
core/src/bitmap.rs View File

@@ -42,10 +42,10 @@ fn invoke_function(
let fn_info = &program.fn_info[fun];
let colour = Colour::new(
ColourFormat::Rgb,
f32::from(bitmap_info.data[index]) / 255.0,
f32::from(bitmap_info.data[index + 1]) / 255.0,
f32::from(bitmap_info.data[index + 2]) / 255.0,
f32::from(bitmap_info.data[index + 3]) / 255.0,
bitmap_info.data[index],
bitmap_info.data[index + 1],
bitmap_info.data[index + 2],
bitmap_info.data[index + 3],
);

vm.function_call_default_arguments(context, program, fn_info)?;
@@ -209,10 +209,10 @@ pub fn value(

let colour = Colour::new(
ColourFormat::Rgb,
f32::from(bitmap_info.data[index]) / 255.0,
f32::from(bitmap_info.data[index + 1]) / 255.0,
f32::from(bitmap_info.data[index + 2]) / 255.0,
f32::from(bitmap_info.data[index + 3]) / 255.0,
bitmap_info.data[index],
bitmap_info.data[index + 1],
bitmap_info.data[index + 2],
bitmap_info.data[index + 3],
);

Ok(colour)

+ 24
- 1
core/src/bitmap_cache.rs View File

@@ -62,9 +62,32 @@ impl BitmapCache {
}
}

// given pixel data as u8 in sRGB colour space
// this converts to in internal linear colour space representation in f32 (range 0..1)
#[derive(Default)]
pub struct BitmapInfo {
pub width: usize,
pub height: usize,
pub data: Vec<u8>,
pub data: Vec<f32>,
}

// https://en.wikipedia.org/wiki/SRGB
fn into_linear_space(c: u8) -> f32 {
let v = f32::from(c) / 255.0;

if v > 0.04045 {
((v + 0.055) / 1.055).powf(2.4)
} else {
v / 12.92
}
}

impl BitmapInfo {
pub fn new(width: usize, height: usize, data: Vec<u8>) -> Self {
BitmapInfo {
width,
height,
data: data.into_iter().map(into_linear_space).collect(),
}
}
}

+ 35
- 1
gui/assets/shaders/blit.frag View File

@@ -9,7 +9,41 @@ uniform sampler2D myTextureSampler;

out vec4 Colour;


vec3 linear_to_srgb(vec3 linear) {
float a = 0.055;
float b = 0.0031308;
vec3 srgb_lo = 12.92 * linear;
vec3 srgb_hi = (1.0 + a) * pow(linear, vec3(1.0/2.4)) - vec3(a);
return vec3(
linear.r > b ? srgb_hi.r : srgb_lo.r,
linear.g > b ? srgb_hi.g : srgb_lo.g,
linear.b > b ? srgb_hi.b : srgb_lo.b);
}

// https://twitter.com/jimhejl/status/633777619998130176
vec3 ToneMapFilmic_Hejl2015(vec3 hdr, float whitePt) {
vec4 vh = vec4(hdr, whitePt);
vec4 va = 1.425 * vh + 0.05;
vec4 vf = (vh * va + 0.004) / (vh * (va + 0.55) + 0.0491) - 0.0821;
return vf.rgb / vf.www;
}



void main()
{
Colour = texture( myTextureSampler, IN.TextureCoord );
// Colour = texture( myTextureSampler, IN.TextureCoord );

// Colour = texture( myTextureSampler, IN.TextureCoord );
// Colour = vec4(clamp(Colour.r, 0.0, 1.0), clamp(Colour.g, 0.0, 1.0), clamp(Colour.b, 0.0, 1.0), 1.0);

// vec4 pieceColour = texture( myTextureSampler, IN.TextureCoord );
// Colour = vec4(linear_to_srgb(ToneMapFilmic_Hejl2015(pieceColour.rgb, 1.0)), 1.0);

vec4 pieceColour = texture( myTextureSampler, IN.TextureCoord );
Colour = vec4(linear_to_srgb(pieceColour.rgb), 1.0);

// vec4 pieceColour = texture( myTextureSampler, IN.TextureCoord );
// Colour = vec4(ToneMapFilmic_Hejl2015(pieceColour.rgb, 1.0), 1.0);
}

+ 11
- 3
gui/src/gl_util.rs View File

@@ -17,13 +17,21 @@ use std::mem;

use std::path::Path;

use core::BitmapInfo;
use gl;
use image::GenericImageView;
use log::info;

use crate::error::{Error, Result};



#[derive(Default)]
pub struct BitmapU8Info {
pub width: usize,
pub height: usize,
pub data: Vec<u8>,
}

#[macro_export]
macro_rules! c_str {
($literal:expr) => {
@@ -40,7 +48,7 @@ where
val
}

pub fn load_texture(ppath: &Path, name: &str) -> Result<BitmapInfo> {
pub fn load_texture(ppath: &Path, name: &str) -> Result<BitmapU8Info> {
let path = ppath.join(name);

info!("load_bitmap: {:?}", path);
@@ -71,7 +79,7 @@ pub fn load_texture(ppath: &Path, name: &str) -> Result<BitmapInfo> {
}
}

let bitmap_info = BitmapInfo {
let bitmap_info = BitmapU8Info {
width,
height,
data: data_flipped,

+ 18
- 18
gui/src/main.rs View File

@@ -381,26 +381,26 @@ fn run(config: &config::Config) -> Result<()> {
// ui.show_demo_window(&mut true);

// ~/repos/rust/imgui-rs/imgui-glium-examples/examples/test_window_impl.rs
ui.window(im_str!("Seni"))
.position((0.0, 0.0), imgui::ImGuiCond::FirstUseEver)
.size((800.0, 600.0), imgui::ImGuiCond::FirstUseEver)
.build(|| {
if ui.button(im_str!("Load.."), (0.0, 0.0)) {
ui.open_popup(im_str!("modal"));
}
// ui.window(im_str!("Seni"))
// .position((0.0, 0.0), imgui::ImGuiCond::FirstUseEver)
// .size((800.0, 600.0), imgui::ImGuiCond::FirstUseEver)
// .build(|| {
// if ui.button(im_str!("Load.."), (0.0, 0.0)) {
// ui.open_popup(im_str!("modal"));
// }

ui.popup_modal(im_str!("modal")).build(|| {
ui.text("Content of my modal");
if ui.button(im_str!("OK"), (0.0, 0.0)) {
ui.close_current_popup();
}
});
// ui.popup_modal(im_str!("modal")).build(|| {
// ui.text("Content of my modal");
// if ui.button(im_str!("OK"), (0.0, 0.0)) {
// ui.close_current_popup();
// }
// });

ui.separator();
if ui.collapsing_header(im_str!("script")).build() {
ui.text(im_str!("{}", seni_source));
}
});
// ui.separator();
// if ui.collapsing_header(im_str!("script")).build() {
// ui.text(im_str!("{}", seni_source));
// }
// });

unsafe {
gl.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

+ 1
- 6
gui/src/seni.rs View File

@@ -48,12 +48,7 @@ fn load_bitmap(asset_prefix: &String, filename: &String, context: &mut Context)
data.push(rgba.data[3]);
}

let bitmap_info = BitmapInfo {
width,
height,
data,
..Default::default()
};
let bitmap_info = BitmapInfo::new(width, height, data);

context.bitmap_cache.insert(&filename, bitmap_info)?;


+ 67
- 0
server/static/seni/sketch/1925-einstein.seni View File

@@ -0,0 +1,67 @@
(define texture "einstein.png")
(define per-pixel-funcs [(address-of pass-0)
(address-of pass-1)
(address-of pass-2)
(address-of pass-3)])
(define rng (prng/build seed: {343 (gen/scalar min: 42 max: 666)}
min: 0
max: 1))

(each (per-pixel-func from: per-pixel-funcs)
(bitmap/each from: texture
position: [500 500]
width: 1000
height: 1000
fn: per-pixel-func))

(fn (pass-0 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.2
width: 1.0
from: [0 0]
to: [1 1]))

(fn (pass-1 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.1
width: {14.3 (gen/scalar min: 0.1 max: 15)}
from: [{10 (gen/scalar min: -15 max: 15)}
{5 (gen/scalar min: -15 max: 15)}]
to: [{-9 (gen/scalar min: -15 max: 15)}
{-1 (gen/scalar min: -15 max: 15)}]))

(fn (pass-2 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.05
width: {2.4 (gen/scalar min: 0.1 max: 15)}
from: [{15 (gen/scalar min: -15 max: 15)}
{-5 (gen/scalar min: -15 max: 15)}]
to: [{0 (gen/scalar min: -15 max: 15)}
{-2 (gen/scalar min: -15 max: 15)}]))

(fn (pass-3 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.025
width: {4.8 (gen/scalar min: 0.1 max: 15)}
from: [{-5 (gen/scalar min: -15 max: 15)}
{-2 (gen/scalar min: -15 max: 15)}]
to: [{-7 (gen/scalar min: -15 max: 15)}
{-3 (gen/scalar min: -15 max: 15)}]))

(fn (per-pixel a: 0 alpha: 1 width: 0 from: [0 0] to: [0 0])
(line from: [(* (nth from: from n: 0) (math/cos angle: (* a math/PI)))
(* (nth from: from n: 1) (math/sin angle: (* a math/PI)))]
to: [(* (nth from: to n: 0) (math/cos angle: (* a math/PI)))
(* (nth from: to n: 1) (math/sin angle: (* a math/PI)))]
width: (+ 0 (- width a))
brush: brush/b
colour: (col/rgb r: a g: a b: a alpha: alpha)))

(fn (per-pixel-rng a: 0 alpha: 1 width: 0 from: [0 0] to: [0 0])
(line from: [(* (prng/value from: rng) (nth from: from n: 0) (math/cos angle: (* a math/PI)))
(* (prng/value from: rng) (nth from: from n: 1) (math/sin angle: (* a math/PI)))]
to: [(* (prng/value from: rng) (nth from: to n: 0) (math/cos angle: (* a math/PI)))
(* (prng/value from: rng) (nth from: to n: 1) (math/sin angle: (* a math/PI)))]
width: (+ 0 (- width a))
brush: brush/b
colour: (col/rgb r: a g: a b: a alpha: alpha)))

+ 67
- 0
server/static/seni/sketch/1925-einstein2.seni View File

@@ -0,0 +1,67 @@
(define texture "einstein.png")
(define per-pixel-funcs [(address-of pass-0)
(address-of pass-1)
(address-of pass-2)
(address-of pass-3)])
(define rng (prng/build seed: {226 (gen/scalar min: 42 max: 666)}
min: 0
max: 1))

(each (per-pixel-func from: per-pixel-funcs)
(bitmap/each from: texture
position: [500 500]
width: 1000
height: 1000
fn: per-pixel-func))

(fn (pass-0 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.2
width: 1.0
from: [0 0]
to: [1 1]))

(fn (pass-1 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel a: (col/get-r colour: colour)
alpha: 0.1
width: {1.5 (gen/scalar min: 0.1 max: 15)}
from: [{-9 (gen/scalar min: -15 max: 15)}
{10 (gen/scalar min: -15 max: 15)}]
to: [{7 (gen/scalar min: -15 max: 15)}
{-14 (gen/scalar min: -15 max: 15)}]))

(fn (pass-2 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel-rng a: (col/get-r colour: colour)
alpha: 0.05
width: {2.9 (gen/scalar min: 0.1 max: 15)}
from: [{12 (gen/scalar min: -15 max: 15)}
{-15 (gen/scalar min: -15 max: 15)}]
to: [{-11 (gen/scalar min: -15 max: 15)}
{9 (gen/scalar min: -15 max: 15)}]))

(fn (pass-3 colour: (col/rgb r: 0 g: 0 b: 0 alpha: 0) position: [100 100])
(per-pixel-rng a: (col/get-r colour: colour)
alpha: 0.025
width: {1.1 (gen/scalar min: 0.1 max: 15)}
from: [{-4 (gen/scalar min: -15 max: 15)}
{-5 (gen/scalar min: -15 max: 15)}]
to: [{11 (gen/scalar min: -15 max: 15)}
{4 (gen/scalar min: -15 max: 15)}]))

(fn (per-pixel a: 0 alpha: 1 width: 0 from: [0 0] to: [0 0])
(line from: [(* (nth from: from n: 0) (math/cos angle: (* a math/PI)))
(* (nth from: from n: 1) (math/sin angle: (* a math/PI)))]
to: [(* (nth from: to n: 0) (math/cos angle: (* a math/PI)))
(* (nth from: to n: 1) (math/sin angle: (* a math/PI)))]
width: (+ 0 (- width a))
brush: brush/b
colour: (col/rgb r: a g: a b: a alpha: alpha)))

(fn (per-pixel-rng a: 0 alpha: 1 width: 0 from: [0 0] to: [0 0])
(line from: [(* (prng/value from: rng) (nth from: from n: 0) (math/cos angle: (* a math/PI)))
(* (prng/value from: rng) (nth from: from n: 1) (math/sin angle: (* a math/PI)))]
to: [(* (prng/value from: rng) (nth from: to n: 0) (math/cos angle: (* a math/PI)))
(* (prng/value from: rng) (nth from: to n: 1) (math/sin angle: (* a math/PI)))]
width: (+ 0 (- width a))
brush: brush/b
colour: (col/rgb r: a g: a b: a alpha: alpha)))

+ 48
- 0
server/static/seni/sketch/1925-shell.seni View File

@@ -0,0 +1,48 @@
(define
coords1 [{[-7.987 -70.872] (gen/stray-2d from: [-3.718 -69.162] by: [5 5])}
{[459.069 -55.678] (gen/stray-2d from: [463.301 -57.804] by: [5 5])}
{[453.886 -313.444] (gen/stray-2d from: [456.097 -315.570] by: [5 5])}
{[318.958 -379.619] (gen/stray-2d from: [318.683 -384.297] by: [5 5])}]

coords2 [{[422.457 21.162] (gen/stray-2d from: [424.112 19.779] by: [5 5])}
{[-0.227 248.540] (gen/stray-2d from: [2.641 246.678] by: [5 5])}
{[-444.511 -82.728] (gen/stray-2d from: [-449.001 -79.842] by: [5 5])}
{[38.476 209.605] (gen/stray-2d from: [37.301 206.818] by: [5 5])}]

coords3 [{[82.956 -286.186] (gen/stray-2d from: [83.331 -282.954] by: [5 5])}
{[88.479 -396.479] (gen/stray-2d from: [92.716 -393.120] by: [5 5])}
{[423.226 -421.275] (gen/stray-2d from: [426.686 -420.284] by: [5 5])}
{[-32.486 338.664] (gen/stray-2d from: [-29.734 335.671] by: [5 5])}]

col-fn-1 (col/build-procedural preset: {transformers (gen/select from: col/procedural-fn-presets)}
alpha: 0.08)
col-fn-2 (col/build-procedural preset: {chrome (gen/select from: col/procedural-fn-presets)}
alpha: 0.08)
col-fn-3 (col/build-procedural preset: {knight-rider (gen/select from: col/procedural-fn-presets)}
alpha: 0.08)

num-copies {25 (gen/int min: 25 max: 30)}
squish (interp/build from: [0 (- num-copies 1)] to: [{1.2 (gen/scalar min: 1.0 max: 1.5)} {0.79 (gen/scalar min: 0.3 max: 1)}]))

(fn (draw angle: 0 copy: 0)
(scale vector: [(interp/value from: squish t: copy) (interp/value from: squish t: copy)])
(fence (t num: 200)
(poly coords: [(interp/bezier t: t coords: coords1)
(interp/bezier t: t coords: coords2)
(interp/bezier t: t coords: coords3)]
colours: [(col/value from: col-fn-1 t: t)
(col/value from: col-fn-2 t: t)
(col/value from: col-fn-3 t: t)])))

(fn (render)
(rect position: [500 500]
width: 1000
height: 1000
colour: (col/value from: col-fn-1 t: 0.5))
(on-matrix-stack
(translate vector: [(/ canvas/width 2) (/ canvas/height 2)])
(scale vector: [0.8 0.8])
(repeat/rotate-mirrored fn: (address-of draw)
copies: num-copies)))

(render)

Loading…
Cancel
Save