mirror of
https://github.com/zellyn/apple2shader.git
synced 2024-12-26 08:29:52 +00:00
webgl: working on renderImage()
This commit is contained in:
parent
75c77e6be1
commit
dba6c94236
@ -224,8 +224,7 @@ void main() {
|
||||
|
||||
async function tryScreenView() {
|
||||
let canvas = document.getElementById("d");
|
||||
let gl = canvas.getContext("webgl");
|
||||
let sv = new screenEmu.ScreenView(gl);
|
||||
let sv = new screenEmu.ScreenView(canvas);
|
||||
await sv.initOpenGL();
|
||||
|
||||
let imageInfo = new screenEmu.ImageInfo(0, 0, 0, 0, 0, 0, new ImageData(10, 10));
|
||||
|
225
screenEmu.js
225
screenEmu.js
@ -244,6 +244,62 @@ void main(void)
|
||||
};
|
||||
}
|
||||
|
||||
const Point = class {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
const Size = class {
|
||||
constructor(width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
copy() {
|
||||
return new Size(this.width, this.height);
|
||||
}
|
||||
}
|
||||
|
||||
const Rect = class {
|
||||
constructor(x, y, width, height) {
|
||||
this.origin = new Point(x, y);
|
||||
this.size = new Size(width, height);
|
||||
}
|
||||
|
||||
get x() {
|
||||
return this.origin.x;
|
||||
}
|
||||
|
||||
get y() {
|
||||
return this.origin.y;
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.size.width;
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.size.height;
|
||||
}
|
||||
|
||||
get l() {
|
||||
return this.origin.x;
|
||||
}
|
||||
|
||||
get r() {
|
||||
return this.origin.x + this.size.width;
|
||||
}
|
||||
|
||||
get t() {
|
||||
return this.origin.y;
|
||||
}
|
||||
|
||||
get b() {
|
||||
return this.origin.y + this.size.height;
|
||||
}
|
||||
}
|
||||
|
||||
const Vector = class {
|
||||
constructor(n) {
|
||||
@ -432,6 +488,11 @@ void main(void)
|
||||
"IMAGE_PERSISTENCE",
|
||||
];
|
||||
|
||||
const BUFFER_NAMES = [
|
||||
"POSITION",
|
||||
"TEXCOORD",
|
||||
];
|
||||
|
||||
const SHADER_NAMES = [
|
||||
"COMPOSITE",
|
||||
"DISPLAY",
|
||||
@ -490,11 +551,15 @@ void main(void)
|
||||
this.height = height;
|
||||
this.glTexture = glTexture;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return new Size(this.width, this.height);
|
||||
}
|
||||
};
|
||||
|
||||
const DisplayConfiguration = class {
|
||||
constructor() {
|
||||
this.videoDecoder = "CANVAS_RGB";
|
||||
this.videoDecoder = "CANVAS_YUV";
|
||||
this.videoBrightness = 0;
|
||||
this.videoContrast = 1;
|
||||
this.videoSaturation = 1;
|
||||
@ -547,26 +612,36 @@ void main(void)
|
||||
get height() {
|
||||
return this.data.height;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return new Size(this.data.width, this.data.height);
|
||||
}
|
||||
};
|
||||
|
||||
const ScreenView = class {
|
||||
constructor(gl) {
|
||||
constructor(canvas) {
|
||||
const gl = canvas.getContext("webgl");
|
||||
const float_texture_ext = gl.getExtension('OES_texture_float');
|
||||
if (float_texture_ext == null) {
|
||||
throw new Error("WebGL extension 'OES_texture_float' unavailable");
|
||||
}
|
||||
|
||||
this.canvas = canvas;
|
||||
this.gl = gl;
|
||||
this.textures = {};
|
||||
this.shaders = {};
|
||||
this.buffers = {};
|
||||
this.image = null;
|
||||
this.display = null;
|
||||
this.configurationChanged = true;
|
||||
this.imageChanged = true;
|
||||
this.imageSampleRate = null;
|
||||
this.imageBlackLevel = null;
|
||||
this.imageWhiteLevel = null;
|
||||
this.imageSubcarrier = null;
|
||||
this.viewportSize = new Size(0, 0);
|
||||
|
||||
this.configurationChanged = true;
|
||||
this.imageChanged = true;
|
||||
this.shaderEnabled = true;
|
||||
}
|
||||
|
||||
get image() {
|
||||
@ -595,6 +670,10 @@ void main(void)
|
||||
this.textures[name] = new TextureInfo(0, 0, gl.createTexture());
|
||||
}
|
||||
|
||||
for (let name of BUFFER_NAMES) {
|
||||
this.buffers[name] = gl.createBuffer();
|
||||
}
|
||||
|
||||
await this.loadTextures();
|
||||
|
||||
gl.pixelStorei(gl.PACK_ALIGNMENT, 1);
|
||||
@ -610,6 +689,10 @@ void main(void)
|
||||
gl.deleteTexture(this.textures[name].glTexture);
|
||||
}
|
||||
|
||||
for (let name of BUFFER_NAMES) {
|
||||
gl.deleteBuffer(this.buffers[name]);
|
||||
}
|
||||
|
||||
this.deleteShaders();
|
||||
}
|
||||
|
||||
@ -663,8 +746,18 @@ void main(void)
|
||||
}
|
||||
|
||||
vsync() {
|
||||
const gl = this.gl;
|
||||
resizeCanvas(this.canvas);
|
||||
const canvasWidth = this.canvas.width;
|
||||
const canvasHeight = this.canvas.height;
|
||||
|
||||
// if viewport size has changed:
|
||||
// glViewPort(0, 0, new_width, new_height);
|
||||
if ((this.viewportSize.width != canvasWidth)
|
||||
|| (this.viewportSize.height != this.canvasHeight)) {
|
||||
this.viewportSize = new Size(canvasWidth, canvasHeight);
|
||||
gl.viewport(0, 0, canvasWidth, canvasHeight);
|
||||
this.configurationChanged = true;
|
||||
}
|
||||
|
||||
if (this.imageChanged) {
|
||||
this.uploadImage();
|
||||
@ -752,7 +845,7 @@ void main(void)
|
||||
if (!renderShader || !displayShader)
|
||||
return;
|
||||
|
||||
const isCompositeDecoder = (renderShaderName == "RGB");
|
||||
const isCompositeDecoder = (renderShaderName == "COMPOSITE");
|
||||
|
||||
// Render shader
|
||||
gl.useProgram(renderShader);
|
||||
@ -967,8 +1060,126 @@ void main(void)
|
||||
this.display.displayLuminanceGain);
|
||||
}
|
||||
|
||||
// TODO(zellyn): implement
|
||||
renderImage() {
|
||||
const gl = this.gl;
|
||||
const [renderShader, renderShaderName] = this.getRenderShader();
|
||||
if (!renderShader || !this.shaderEnabled)
|
||||
return;
|
||||
|
||||
const isCompositeDecoder = (renderShaderName == "COMPOSITE");
|
||||
|
||||
gl.useProgram(renderShader);
|
||||
|
||||
const texSize = this.textures["IMAGE_IN"].size;
|
||||
this.resizeTexture("IMAGE_DECODED", texSize.width, texSize.height);
|
||||
|
||||
gl.uniform1i(gl.getUniformLocation(renderShader, "texture"), 0);
|
||||
gl.uniform2f(gl.getUniformLocation(renderShader, "textureSize"),
|
||||
texSize.width, texSize.height);
|
||||
|
||||
if (isCompositeDecoder)
|
||||
{
|
||||
gl.uniform1i(gl.getUniformLocation(renderShader, "phaseInfo"), 1);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.textures["IMAGE_PHASEINFO"].glTexture);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
}
|
||||
|
||||
// Render to the back buffer, to avoid using FBOs
|
||||
// (support for vanilla OpenGL 2.0 cards)
|
||||
|
||||
// I think webgl is rendering to the back buffer anyway, until
|
||||
// we stop executing javascript statements and give control
|
||||
// back, at which point it flips. So we might not need
|
||||
// this. Although truly, I'm not certain what it's doing. If we
|
||||
// *do* end up needing it, we'll have to go full webgl2.
|
||||
|
||||
// glReadBuffer(GL_BACK);
|
||||
|
||||
const imageSize = this.image.size;
|
||||
|
||||
for (let y = 0; y < this.image.height; y += this.viewportSize.height) {
|
||||
for (let x = 0; x < this.image.width; x += this.viewportSize.width) {
|
||||
// Calculate rects
|
||||
const clipSize = this.viewportSize.copy();
|
||||
|
||||
if ((x + clipSize.width) > imageSize.width)
|
||||
clipSize.width = imageSize.width - x;
|
||||
if ((y + clipSize.height) > imageSize.height)
|
||||
clipSize.height = imageSize.height - y;
|
||||
const textureRect = new Rect(x / texSize.width,
|
||||
y / texSize.height,
|
||||
clipSize.width / texSize.width,
|
||||
clipSize.height / texSize.height);
|
||||
const canvasRect = new Rect(-1,
|
||||
-1,
|
||||
2 * clipSize.width / this.viewportSize.width,
|
||||
2 * clipSize.height / this.viewportSize.height);
|
||||
|
||||
// Render
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.textures["IMAGE_IN"].glTexture);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
|
||||
// What is this, some ancient fixed pipeline nonsense? No way.
|
||||
// glLoadIdentity();
|
||||
|
||||
const positionLocation = gl.getAttribLocation(renderShader, "a_position");
|
||||
const texcoordLocation = gl.getAttribLocation(renderShader, "a_texCoord");
|
||||
const positionBuffer = this.buffers["POSITION"];
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
const p_x1 = canvasRect.l;
|
||||
const p_x2 = canvasRect.r;
|
||||
const p_y1 = canvasRect.t;
|
||||
const p_y2 = canvasRect.b;
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||
p_x1, p_y1,
|
||||
p_x2, p_y1,
|
||||
p_x1, p_y2,
|
||||
p_x1, p_y2,
|
||||
p_x2, p_y1,
|
||||
p_x2, p_y2,
|
||||
]), gl.STATIC_DRAW);
|
||||
|
||||
const texcoordBuffer = this.buffers["TEXCOORD"];
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
||||
const t_x1 = textureRect.l;
|
||||
const t_x2 = textureRect.r;
|
||||
const t_y1 = textureRect.t;
|
||||
const t_y2 = textureRect.b;
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||
t_x1, t_y1,
|
||||
t_x2, t_y1,
|
||||
t_x1, t_y2,
|
||||
t_x1, t_y2,
|
||||
t_x2, t_y1,
|
||||
t_x2, t_y2,
|
||||
]), gl.STATIC_DRAW);
|
||||
|
||||
gl.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.enableVertexAttribArray(positionLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.enableVertexAttribArray(texcoordLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
||||
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO(zellyn): implement
|
||||
}
|
||||
|
||||
// TODO(zellyn): implement
|
||||
|
Loading…
Reference in New Issue
Block a user