mirror of
https://github.com/zellyn/apple2shader.git
synced 2024-06-09 20:29:31 +00:00
webgl: moving on to configureShaders
This commit is contained in:
parent
3e9246fae6
commit
3ed59c0509
|
@ -225,7 +225,7 @@ void main() {
|
||||||
async function tryScreenView() {
|
async function tryScreenView() {
|
||||||
let canvas = document.getElementById("d");
|
let canvas = document.getElementById("d");
|
||||||
let gl = canvas.getContext("webgl");
|
let gl = canvas.getContext("webgl");
|
||||||
let sv = screenEmu.getScreenView(gl);
|
let sv = new screenEmu.ScreenView(gl);
|
||||||
await sv.initOpenGL();
|
await sv.initOpenGL();
|
||||||
sv.freeOpenGL();
|
sv.freeOpenGL();
|
||||||
}
|
}
|
||||||
|
|
311
screenEmu.js
311
screenEmu.js
|
@ -1,63 +1,63 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let screenEmu = (function () {
|
const screenEmu = (function () {
|
||||||
// From AppleIIVideo.cpp
|
// From AppleIIVideo.cpp
|
||||||
|
|
||||||
let HORIZ_START = 16;
|
const HORIZ_START = 16;
|
||||||
let HORIZ_BLANK = (9 + HORIZ_START) // 25;
|
const HORIZ_BLANK = (9 + HORIZ_START) // 25;
|
||||||
let HORIZ_DISPLAY = 40;
|
const HORIZ_DISPLAY = 40;
|
||||||
let HORIZ_TOTAL = (HORIZ_BLANK + HORIZ_DISPLAY) // 65;
|
const HORIZ_TOTAL = (HORIZ_BLANK + HORIZ_DISPLAY) // 65;
|
||||||
|
|
||||||
let CELL_WIDTH = 14;
|
const CELL_WIDTH = 14;
|
||||||
let CELL_HEIGHT = 8;
|
const CELL_HEIGHT = 8;
|
||||||
|
|
||||||
let VERT_NTSC_START = 38;
|
const VERT_NTSC_START = 38;
|
||||||
let VERT_PAL_START = 48;
|
const VERT_PAL_START = 48;
|
||||||
let VERT_DISPLAY = 192;
|
const VERT_DISPLAY = 192;
|
||||||
|
|
||||||
let BLOCK_WIDTH = HORIZ_DISPLAY; // 40
|
const BLOCK_WIDTH = HORIZ_DISPLAY; // 40
|
||||||
let BLOCK_HEIGHT = (VERT_DISPLAY / CELL_HEIGHT); // 24
|
const BLOCK_HEIGHT = (VERT_DISPLAY / CELL_HEIGHT); // 24
|
||||||
|
|
||||||
// From CanvasInterface.h
|
// From CanvasInterface.h
|
||||||
|
|
||||||
let NTSC_FSC = 315/88 * 1e6; // 3579545 = 3.5 Mhz: Color Subcarrier
|
const NTSC_FSC = 315/88 * 1e6; // 3579545 = 3.5 Mhz: Color Subcarrier
|
||||||
let NTSC_4FSC = 4 * NTSC_FSC; // 14318180 = 14.3 Mhz
|
const NTSC_4FSC = 4 * NTSC_FSC; // 14318180 = 14.3 Mhz
|
||||||
let NTSC_HTOTAL = (63+5/9) * 1e-6;
|
const NTSC_HTOTAL = (63+5/9) * 1e-6;
|
||||||
let NTSC_HLENGTH = (52+8/9) * 1e-6;
|
const NTSC_HLENGTH = (52+8/9) * 1e-6;
|
||||||
let NTSC_HHALF = (35+2/3) * 1e-6;
|
const NTSC_HHALF = (35+2/3) * 1e-6;
|
||||||
let NTSC_HSTART = NTSC_HHALF - NTSC_HLENGTH/2;
|
const NTSC_HSTART = NTSC_HHALF - NTSC_HLENGTH/2;
|
||||||
let NTSC_HEND = NTSC_HHALF + NTSC_HLENGTH/2;
|
const NTSC_HEND = NTSC_HHALF + NTSC_HLENGTH/2;
|
||||||
let NTSC_VTOTAL = 262;
|
const NTSC_VTOTAL = 262;
|
||||||
let NTSC_VLENGTH = 240;
|
const NTSC_VLENGTH = 240;
|
||||||
let NTSC_VSTART = 19;
|
const NTSC_VSTART = 19;
|
||||||
let NTSC_VEND = NTSC_VSTART + NTSC_VLENGTH;
|
const NTSC_VEND = NTSC_VSTART + NTSC_VLENGTH;
|
||||||
|
|
||||||
let PAL_FSC = 4433618.75; // Color subcarrier
|
const PAL_FSC = 4433618.75; // Color subcarrier
|
||||||
let PAL_4FSC = 4 * PAL_FSC;
|
const PAL_4FSC = 4 * PAL_FSC;
|
||||||
let PAL_HTOTAL = 64e-6;
|
const PAL_HTOTAL = 64e-6;
|
||||||
let PAL_HLENGTH = 52e-6;
|
const PAL_HLENGTH = 52e-6;
|
||||||
let PAL_HHALF = (37+10/27) * 1e-6;
|
const PAL_HHALF = (37+10/27) * 1e-6;
|
||||||
let PAL_HSTART = PAL_HHALF - PAL_HLENGTH / 2;
|
const PAL_HSTART = PAL_HHALF - PAL_HLENGTH / 2;
|
||||||
let PAL_HEND = PAL_HHALF + PAL_HLENGTH / 2;
|
const PAL_HEND = PAL_HHALF + PAL_HLENGTH / 2;
|
||||||
let PAL_VTOTAL = 312;
|
const PAL_VTOTAL = 312;
|
||||||
let PAL_VLENGTH = 288;
|
const PAL_VLENGTH = 288;
|
||||||
let PAL_VSTART = 21;
|
const PAL_VSTART = 21;
|
||||||
let PAL_VEND = PAL_VSTART + PAL_VLENGTH;
|
const PAL_VEND = PAL_VSTART + PAL_VLENGTH;
|
||||||
|
|
||||||
// From AppleIIVideo::updateTiming
|
// From AppleIIVideo::updateTiming
|
||||||
let ntscClockFrequency = NTSC_4FSC * HORIZ_TOTAL / 912;
|
const ntscClockFrequency = NTSC_4FSC * HORIZ_TOTAL / 912;
|
||||||
let ntscVisibleRect = [[ntscClockFrequency * NTSC_HSTART, NTSC_VSTART],
|
const ntscVisibleRect = [[ntscClockFrequency * NTSC_HSTART, NTSC_VSTART],
|
||||||
[ntscClockFrequency * NTSC_HLENGTH, NTSC_VLENGTH]];
|
[ntscClockFrequency * NTSC_HLENGTH, NTSC_VLENGTH]];
|
||||||
let ntscDisplayRect = [[HORIZ_START, VERT_NTSC_START],
|
const ntscDisplayRect = [[HORIZ_START, VERT_NTSC_START],
|
||||||
[HORIZ_DISPLAY, VERT_DISPLAY]];
|
[HORIZ_DISPLAY, VERT_DISPLAY]];
|
||||||
let ntscVertTotal = NTSC_VTOTAL;
|
const ntscVertTotal = NTSC_VTOTAL;
|
||||||
|
|
||||||
let palClockFrequency = 14250450.0 * HORIZ_TOTAL / 912;
|
const palClockFrequency = 14250450.0 * HORIZ_TOTAL / 912;
|
||||||
let palVisibleRect = [[palClockFrequency * PAL_HSTART, PAL_VSTART],
|
const palVisibleRect = [[palClockFrequency * PAL_HSTART, PAL_VSTART],
|
||||||
[palClockFrequency * PAL_HLENGTH, PAL_VLENGTH]];
|
[palClockFrequency * PAL_HLENGTH, PAL_VLENGTH]];
|
||||||
let palDisplayRect = [[HORIZ_START, VERT_PAL_START],
|
const palDisplayRect = [[HORIZ_START, VERT_PAL_START],
|
||||||
[HORIZ_DISPLAY, VERT_DISPLAY]];
|
[HORIZ_DISPLAY, VERT_DISPLAY]];
|
||||||
let palVertTotal = PAL_VTOTAL;
|
const palVertTotal = PAL_VTOTAL;
|
||||||
|
|
||||||
const VERTEX_SHADER =`
|
const VERTEX_SHADER =`
|
||||||
// an attribute will receive data from a buffer
|
// an attribute will receive data from a buffer
|
||||||
|
@ -77,6 +77,8 @@ void main() {
|
||||||
const COMPOSITE_SHADER = `
|
const COMPOSITE_SHADER = `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
uniform vec2 textureSize;
|
uniform vec2 textureSize;
|
||||||
uniform float subcarrier;
|
uniform float subcarrier;
|
||||||
|
@ -84,7 +86,6 @@ uniform sampler2D phaseInfo;
|
||||||
uniform vec3 c0, c1, c2, c3, c4, c5, c6, c7, c8;
|
uniform vec3 c0, c1, c2, c3, c4, c5, c6, c7, c8;
|
||||||
uniform mat3 decoderMatrix;
|
uniform mat3 decoderMatrix;
|
||||||
uniform vec3 decoderOffset;
|
uniform vec3 decoderOffset;
|
||||||
varying vec2 v_texCoord;
|
|
||||||
|
|
||||||
float PI = 3.14159265358979323846264;
|
float PI = 3.14159265358979323846264;
|
||||||
|
|
||||||
|
@ -120,6 +121,8 @@ void main(void)
|
||||||
const DISPLAY_SHADER = `
|
const DISPLAY_SHADER = `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
uniform vec2 textureSize;
|
uniform vec2 textureSize;
|
||||||
uniform float barrel;
|
uniform float barrel;
|
||||||
|
@ -134,7 +137,6 @@ uniform vec2 persistenceSize;
|
||||||
uniform vec2 persistenceOrigin;
|
uniform vec2 persistenceOrigin;
|
||||||
uniform float persistenceLevel;
|
uniform float persistenceLevel;
|
||||||
uniform float luminanceGain;
|
uniform float luminanceGain;
|
||||||
varying vec2 v_texCoord;
|
|
||||||
|
|
||||||
float PI = 3.14159265358979323846264;
|
float PI = 3.14159265358979323846264;
|
||||||
|
|
||||||
|
@ -162,26 +164,63 @@ void main(void)
|
||||||
|
|
||||||
gl_FragColor = vec4(c, 1.0);
|
gl_FragColor = vec4(c, 1.0);
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RGB_SHADER = `
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform vec2 textureSize;
|
||||||
|
uniform vec3 c0, c1, c2, c3, c4, c5, c6, c7, c8;
|
||||||
|
uniform mat3 decoderMatrix;
|
||||||
|
uniform vec3 decoderOffset;
|
||||||
|
|
||||||
|
vec3 pixel(vec2 q)
|
||||||
|
{
|
||||||
|
return texture2D(texture, q).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 pixels(in vec2 q, in float i)
|
||||||
|
{
|
||||||
|
return pixel(vec2(q.x + i, q.y)) + pixel(vec2(q.x - i, q.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec2 q = v_texCoord;
|
||||||
|
vec3 c = pixel(q) * c0;
|
||||||
|
c += pixels(q, 1.0 / textureSize.x) * c1;
|
||||||
|
c += pixels(q, 2.0 / textureSize.x) * c2;
|
||||||
|
c += pixels(q, 3.0 / textureSize.x) * c3;
|
||||||
|
c += pixels(q, 4.0 / textureSize.x) * c4;
|
||||||
|
c += pixels(q, 5.0 / textureSize.x) * c5;
|
||||||
|
c += pixels(q, 6.0 / textureSize.x) * c6;
|
||||||
|
c += pixels(q, 7.0 / textureSize.x) * c7;
|
||||||
|
c += pixels(q, 8.0 / textureSize.x) * c8;
|
||||||
|
gl_FragColor = vec4(decoderMatrix * c + decoderOffset, 1.0);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function buildTiming(clockFrequency, displayRect, visibleRect, vertTotal) {
|
function buildTiming(clockFrequency, displayRect, visibleRect, vertTotal) {
|
||||||
let vertStart = displayRect[0][1];
|
const vertStart = displayRect[0][1];
|
||||||
// Total number of CPU cycles per frame: 17030 for NTSC.
|
// Total number of CPU cycles per frame: 17030 for NTSC.
|
||||||
let frameCycleNum = HORIZ_TOTAL * vertTotal;
|
const frameCycleNum = HORIZ_TOTAL * vertTotal;
|
||||||
// first displayed column.
|
// first displayed column.
|
||||||
let horizStart = Math.floor(displayRect[0][0]);
|
const horizStart = Math.floor(displayRect[0][0]);
|
||||||
// imageSize is [14 * visible rect width in cells, visible lines]
|
// imageSize is [14 * visible rect width in cells, visible lines]
|
||||||
let imageSize = [Math.floor(CELL_WIDTH * visibleRect[1][0]),
|
const imageSize = [Math.floor(CELL_WIDTH * visibleRect[1][0]),
|
||||||
Math.floor(visibleRect[1][1])];
|
Math.floor(visibleRect[1][1])];
|
||||||
// imageLeft is # of pixels from first visible point to first displayed point.
|
// imageLeft is # of pixels from first visible point to first displayed point.
|
||||||
let imageLeft = Math.floor((horizStart-visibleRect[0][0]) * CELL_WIDTH);
|
const imageLeft = Math.floor((horizStart-visibleRect[0][0]) * CELL_WIDTH);
|
||||||
let colorBurst = [2 * Math.PI * (-33/360 + (imageLeft % 4) / 4)];
|
const colorBurst = [2 * Math.PI * (-33/360 + (imageLeft % 4) / 4)];
|
||||||
let cycleNum = frameCycleNum + 16;
|
const cycleNum = frameCycleNum + 16;
|
||||||
|
|
||||||
// First pixel that OpenEmulator draws when painting normally.
|
// First pixel that OpenEmulator draws when painting normally.
|
||||||
let topLeft = [imageLeft, vertStart - visibleRect[0][1]];
|
const topLeft = [imageLeft, vertStart - visibleRect[0][1]];
|
||||||
// First pixel that OpenEmulator draws when painting 80-column mode.
|
// First pixel that OpenEmulator draws when painting 80-column mode.
|
||||||
let topLeft80Col = [imageLeft - CELL_WIDTH/2, vertStart - visibleRect[0][1]];
|
const topLeft80Col = [imageLeft - CELL_WIDTH/2, vertStart - visibleRect[0][1]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clockFrequency: clockFrequency,
|
clockFrequency: clockFrequency,
|
||||||
|
@ -218,16 +257,16 @@ void main(void)
|
||||||
if ((image.naturalWidth != 560) || (image.naturalHeight != 192)) {
|
if ((image.naturalWidth != 560) || (image.naturalHeight != 192)) {
|
||||||
throw `screenData expects an image 560x192; got ${image.naturalWidth}x${image.naturalHeight}`;
|
throw `screenData expects an image 560x192; got ${image.naturalWidth}x${image.naturalHeight}`;
|
||||||
}
|
}
|
||||||
let canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
let context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
let width = details.imageSize[0];
|
const width = details.imageSize[0];
|
||||||
let height = details.imageSize[1];
|
const height = details.imageSize[1];
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
context.fillStyle = 'rgba(0,0,0,1)';
|
context.fillStyle = 'rgba(0,0,0,1)';
|
||||||
context.fillRect(0, 0, width, height);
|
context.fillRect(0, 0, width, height);
|
||||||
context.drawImage(image, details.topLeft80Col[0], details.topLeft80Col[1]);
|
context.drawImage(image, details.topLeft80Col[0], details.topLeft80Col[1]);
|
||||||
// let myData = context.getImageData(0, 0, image.naturalWidth, image.naturalHeight);
|
// const myData = context.getImageData(0, 0, image.naturalWidth, image.naturalHeight);
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -246,12 +285,13 @@ void main(void)
|
||||||
const SHADER_NAMES = [
|
const SHADER_NAMES = [
|
||||||
"COMPOSITE",
|
"COMPOSITE",
|
||||||
"DISPLAY",
|
"DISPLAY",
|
||||||
|
"RGB",
|
||||||
];
|
];
|
||||||
|
|
||||||
const resizeCanvas = (canvas) => {
|
const resizeCanvas = (canvas) => {
|
||||||
// Lookup the size the browser is displaying the canvas.
|
// Lookup the size the browser is displaying the canvas.
|
||||||
let displayWidth = canvas.clientWidth;
|
const displayWidth = canvas.clientWidth;
|
||||||
let displayHeight = canvas.clientHeight;
|
const displayHeight = canvas.clientHeight;
|
||||||
|
|
||||||
// Check if the canvas is not the same size.
|
// Check if the canvas is not the same size.
|
||||||
if (canvas.width != displayWidth ||
|
if (canvas.width != displayWidth ||
|
||||||
|
@ -264,15 +304,15 @@ void main(void)
|
||||||
// Code from:
|
// Code from:
|
||||||
// https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
|
// https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
|
||||||
const createShader = (gl, name, type, source) => {
|
const createShader = (gl, name, type, source) => {
|
||||||
let shader = gl.createShader(type);
|
const shader = gl.createShader(type);
|
||||||
gl.shaderSource(shader, source);
|
gl.shaderSource(shader, source);
|
||||||
gl.compileShader(shader);
|
gl.compileShader(shader);
|
||||||
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||||||
if (success) {
|
if (success) {
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
let log = gl.getShaderInfoLog(shader);
|
const log = gl.getShaderInfoLog(shader);
|
||||||
gl.deleteShader(shader);
|
gl.deleteShader(shader);
|
||||||
throw `unable to compile shader ${name}: \n${log}`;
|
throw `unable to compile shader ${name}: \n${log}`;
|
||||||
};
|
};
|
||||||
|
@ -280,16 +320,16 @@ void main(void)
|
||||||
// Code from:
|
// Code from:
|
||||||
// https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
|
// https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
|
||||||
const createProgram = (gl, name, ...shaders) => {
|
const createProgram = (gl, name, ...shaders) => {
|
||||||
let program = gl.createProgram();
|
const program = gl.createProgram();
|
||||||
for (let shader of shaders) {
|
for (let shader of shaders) {
|
||||||
gl.attachShader(program, shader);
|
gl.attachShader(program, shader);
|
||||||
}
|
}
|
||||||
gl.linkProgram(program);
|
gl.linkProgram(program);
|
||||||
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
|
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
|
||||||
if (success) {
|
if (success) {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
let log = gl.getProgramInfoLog(program);
|
const log = gl.getProgramInfoLog(program);
|
||||||
gl.deleteProgram(program);
|
gl.deleteProgram(program);
|
||||||
throw `unable to compile program ${name}: \n${log}`;
|
throw `unable to compile program ${name}: \n${log}`;
|
||||||
};
|
};
|
||||||
|
@ -302,6 +342,36 @@ void main(void)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DisplayConfiguration = class {
|
||||||
|
constructor() {
|
||||||
|
this.videoDecoder = "CANVAS_RGB";
|
||||||
|
this.videoBrightness = 0;
|
||||||
|
this.videoContrast = 1;
|
||||||
|
this.videoSaturation = 1;
|
||||||
|
this.videoHue = 0;
|
||||||
|
this.videoCenter = [0,0];
|
||||||
|
this.videoSize = [1,1];
|
||||||
|
this.videoBandwidth = 14318180;
|
||||||
|
this.videoLumaBandwidth = 600000;
|
||||||
|
this.videoChromaBandwidth = 2000000;
|
||||||
|
this.videoWhiteOnly = false;
|
||||||
|
|
||||||
|
this.displayResolution = [640, 480];
|
||||||
|
this.displayPixelDensity = 72;
|
||||||
|
this.displayBarrel = 0;
|
||||||
|
this.displayScanlineLevel = 0;
|
||||||
|
this.displayShadowMaskLevel = 0;
|
||||||
|
this.displayShadowMaskDotPitch = 1;
|
||||||
|
this.displayShadowMask = "SHADOWMASK_TRIAD";
|
||||||
|
this.displayPersistence = 0;
|
||||||
|
this.displayCenterLighting = 1;
|
||||||
|
this.displayLuminanceGain = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Corresponds to OEImage. Contains the data on an NTSC/PAL/whatever
|
||||||
|
// image. The `data` field is an ImageData object with the actual
|
||||||
|
// image data.
|
||||||
const ImageInfo = class {
|
const ImageInfo = class {
|
||||||
constructor(sampleRate, blackLevel, whiteLevel, subCarrier, colorBurst,
|
constructor(sampleRate, blackLevel, whiteLevel, subCarrier, colorBurst,
|
||||||
phaseAlternation, data) {
|
phaseAlternation, data) {
|
||||||
|
@ -315,13 +385,11 @@ void main(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
// TODO(zellyn): implement
|
return this.data.width;
|
||||||
return 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
// TODO(zellyn): implement
|
return this.data.height;
|
||||||
return 100;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -338,8 +406,13 @@ void main(void)
|
||||||
this.imageChanged = true;
|
this.imageChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set displayConfiguration(displayConfiguration) {
|
||||||
|
this.display = displayConfiguration;
|
||||||
|
this.configurationChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
async initOpenGL() {
|
async initOpenGL() {
|
||||||
let gl = this.gl;
|
const gl = this.gl;
|
||||||
|
|
||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
@ -359,7 +432,7 @@ void main(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
freeOpenGL() {
|
freeOpenGL() {
|
||||||
let gl = this.gl;
|
const gl = this.gl;
|
||||||
|
|
||||||
for (let name of TEXTURE_NAMES) {
|
for (let name of TEXTURE_NAMES) {
|
||||||
gl.deleteTexture(this.textures[name].glTexture);
|
gl.deleteTexture(this.textures[name].glTexture);
|
||||||
|
@ -379,9 +452,9 @@ void main(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadTexture(path, isMipMap, name) {
|
async loadTexture(path, isMipMap, name) {
|
||||||
let gl = this.gl;
|
const gl = this.gl;
|
||||||
let texInfo = this.textures[name];
|
const texInfo = this.textures[name];
|
||||||
let image = await loadImage(path);
|
const image = await loadImage(path);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texInfo.glTexture);
|
gl.bindTexture(gl.TEXTURE_2D, texInfo.glTexture);
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
||||||
gl.RGBA, gl.UNSIGNED_BYTE, image);
|
gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||||
|
@ -396,14 +469,15 @@ void main(void)
|
||||||
loadShaders() {
|
loadShaders() {
|
||||||
this.loadShader("COMPOSITE", COMPOSITE_SHADER);
|
this.loadShader("COMPOSITE", COMPOSITE_SHADER);
|
||||||
this.loadShader("DISPLAY", DISPLAY_SHADER);
|
this.loadShader("DISPLAY", DISPLAY_SHADER);
|
||||||
|
this.loadShader("RGB", RGB_SHADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadShader(name, source) {
|
loadShader(name, source) {
|
||||||
console.log(`ScreenView.loadShader(${name}): not implemented yet`);
|
console.log(`ScreenView.loadShader(${name}): not implemented yet`);
|
||||||
|
|
||||||
let glVertexShader = createShader(this.gl, name, this.gl.VERTEX_SHADER, VERTEX_SHADER);
|
const glVertexShader = createShader(this.gl, name, this.gl.VERTEX_SHADER, VERTEX_SHADER);
|
||||||
let glFragmentShader = createShader(this.gl, name, this.gl.FRAGMENT_SHADER, source);
|
const glFragmentShader = createShader(this.gl, name, this.gl.FRAGMENT_SHADER, source);
|
||||||
let glProgram = createProgram(this.gl, name, glVertexShader, glFragmentShader);
|
const glProgram = createProgram(this.gl, name, glVertexShader, glFragmentShader);
|
||||||
this.gl.deleteShader(glVertexShader);
|
this.gl.deleteShader(glVertexShader);
|
||||||
this.gl.deleteShader(glFragmentShader);
|
this.gl.deleteShader(glFragmentShader);
|
||||||
this.shaders[name] = glProgram;
|
this.shaders[name] = glProgram;
|
||||||
|
@ -418,7 +492,6 @@ void main(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(zellyn): implement
|
|
||||||
vsync() {
|
vsync() {
|
||||||
// if viewport size has changed:
|
// if viewport size has changed:
|
||||||
// glViewPort(0, 0, new_width, new_height);
|
// glViewPort(0, 0, new_width, new_height);
|
||||||
|
@ -427,27 +500,31 @@ void main(void)
|
||||||
this.uploadImage();
|
this.uploadImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if configuration updated:
|
if (this.configurationChanged) {
|
||||||
// configureShaders();
|
this.configureShaders();
|
||||||
|
}
|
||||||
|
|
||||||
// if image or configuration updated:
|
if (this.imageChanged || this.configurationChanged) {
|
||||||
// renderImage();
|
this.renderImage();
|
||||||
|
}
|
||||||
|
|
||||||
// if anything updated, or displayPersistence != 0.0
|
if (this.imageChanged || this.configurationChanged ||
|
||||||
// drawDisplayCanvas();
|
this.image.displayPersistence != 0) {
|
||||||
|
this.drawDisplayCanvas();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadImage() {
|
uploadImage() {
|
||||||
let image = this.imageInfo;
|
const image = this.imageInfo;
|
||||||
|
|
||||||
this.resizeTexture("IMAGE_IN", image.width, image.height);
|
this.resizeTexture("IMAGE_IN", image.width, image.height);
|
||||||
let texInfo = this.textures["IMAGE_IN"];
|
const texInfo = this.textures["IMAGE_IN"];
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texInfo.glTexture);
|
gl.bindTexture(gl.TEXTURE_2D, texInfo.glTexture);
|
||||||
let format = gl.XYZZY;
|
const format = gl.LUMINANCE;
|
||||||
gl.texSubImage(gl.TEXTURE_2D, 0,
|
const type = gl.UNSIGNED_BYTE;
|
||||||
0, 0,
|
gl.texSubImage2D(gl.TEXTURE_2D, 0,
|
||||||
image.width, image.height,
|
0, 0, // xoffset, yoffset
|
||||||
format, gl.UNSIGNED_BYTE, image.pixels);
|
format, type, image.data);
|
||||||
|
|
||||||
// Update configuration
|
// Update configuration
|
||||||
if ((image.sampleRate != this.imageSampleRate) ||
|
if ((image.sampleRate != this.imageSampleRate) ||
|
||||||
|
@ -460,18 +537,18 @@ void main(void)
|
||||||
this.imageWhiteLevel = image.whiteLevel;
|
this.imageWhiteLevel = image.whiteLevel;
|
||||||
this.imageSubcarrier = image.subcarrier;
|
this.imageSubcarrier = image.subcarrier;
|
||||||
|
|
||||||
this.isConfigurationUpdated = true;
|
this.configurationChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload phase info
|
// Upload phase info
|
||||||
let texHeight = 2**Math.ceil(Math.log2(image.height));
|
const texHeight = 2**Math.ceil(Math.log2(image.height));
|
||||||
let colorBurst = image.colorBurst
|
const colorBurst = image.colorBurst
|
||||||
let phaseAlternation = image.phaseAlternation;
|
const phaseAlternation = image.phaseAlternation;
|
||||||
|
|
||||||
let phaseInfo = new Float32Array(3 * texHeight);
|
const phaseInfo = new Float32Array(3 * texHeight);
|
||||||
|
|
||||||
for (let x = 0; x < image.height; x++) {
|
for (let x = 0; x < image.height; x++) {
|
||||||
let c = colorBurst[x % colorBurst.length] / 2 / Math.PI;
|
const c = colorBurst[x % colorBurst.length] / 2 / Math.PI;
|
||||||
phaseInfo[3 * x + 0] = c - Math.floor(c);
|
phaseInfo[3 * x + 0] = c - Math.floor(c);
|
||||||
phaseInfo[3 * x + 1] = phaseAlternation[x % phaseAlternation.length];
|
phaseInfo[3 * x + 1] = phaseAlternation[x % phaseAlternation.length];
|
||||||
}
|
}
|
||||||
|
@ -482,8 +559,34 @@ void main(void)
|
||||||
gl.RGB, gl.FLOAT, phaseInfo);
|
gl.RGB, gl.FLOAT, phaseInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(zellyn): implement
|
getRenderShader() {
|
||||||
|
switch (this.display.videoDecoder) {
|
||||||
|
case "CANVAS_RGB":
|
||||||
|
case "CANVAS_MONOCHROME":
|
||||||
|
return [this.shaders["RGB"], "RGB"];
|
||||||
|
case "CANVAS_YUV":
|
||||||
|
case "CANVAS_YIQ":
|
||||||
|
case "CANVAS_CXA2025AS":
|
||||||
|
return [this.shaders["COMPOSITE"], "COMPOSITE"];
|
||||||
|
}
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
configureShaders() {
|
configureShaders() {
|
||||||
|
const gl = this.gl;
|
||||||
|
|
||||||
|
const [renderShader, renderShaderName] = this.getRenderShader();
|
||||||
|
const displayShader = this.shaders["DISPLAY"];
|
||||||
|
|
||||||
|
if (!renderShader || !displayShader)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const isCompositeShader = (renderShaderName == "RGB");
|
||||||
|
|
||||||
|
// Render shader
|
||||||
|
gl.useProgram(renderShader);
|
||||||
|
|
||||||
|
// TODO(zellyn): implement the rest
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(zellyn): implement
|
// TODO(zellyn): implement
|
||||||
|
@ -498,8 +601,8 @@ void main(void)
|
||||||
// highest power of two width and height. Wouldn't be
|
// highest power of two width and height. Wouldn't be
|
||||||
// necessary with webgl2.
|
// necessary with webgl2.
|
||||||
resizeTexture(name, width, height) {
|
resizeTexture(name, width, height) {
|
||||||
let gl = this.gl;
|
const gl = this.gl;
|
||||||
let texInfo = this.textures[name];
|
const texInfo = this.textures[name];
|
||||||
if (!!texInfo) {
|
if (!!texInfo) {
|
||||||
throw `Cannot find texture named ${name}`;
|
throw `Cannot find texture named ${name}`;
|
||||||
}
|
}
|
||||||
|
@ -538,9 +641,13 @@ void main(void)
|
||||||
},
|
},
|
||||||
loadImage: loadImage,
|
loadImage: loadImage,
|
||||||
screenData: screenData,
|
screenData: screenData,
|
||||||
getScreenView: (gl) => new ScreenView(gl),
|
|
||||||
resizeCanvas: resizeCanvas,
|
resizeCanvas: resizeCanvas,
|
||||||
createShader: createShader,
|
createShader: createShader,
|
||||||
createProgram: createProgram,
|
createProgram: createProgram,
|
||||||
|
|
||||||
|
// Classes.
|
||||||
|
ScreenView: ScreenView,
|
||||||
|
DisplayConfiguration: DisplayConfiguration,
|
||||||
|
ImageInfo: ImageInfo,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user