gpt4 book ai didi

javascript - 如何在 JS 中正确编写此着色器函数?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:16:40 25 4
gpt4 key购买 nike

我想要发生的事情:

为了测试我想到的游戏艺术风格,我想以像素艺术形式渲染 3D 世界。因此,例如,拍摄这样的场景(但使用特定的颜色/样式渲染以便在像素化后看起来不错):

Original Image

让它看起来像这样:

Pixelated Image

通过尝试不同的 3D 源样式样式,我认为像素化输出看起来不错。当然,要获得这种效果,只需将图像缩小到 ~80p,然后使用最近邻重采样将其放大到 1080p。但直接渲染到 80p Canvas 并进行放大会更有效。

这通常不是人们使用着色器以最近邻格式调整位图大小的方式,但它的性能优于我发现的实时进行此类转换的任何其他方式。

我的代码:

我的位图缓冲区存储在主行中,如 r1, g1, b1, a1, r2, g2, b2, a2... 我正在使用 gpu.js它实质上将这个 JS func 转换为着色器。我的目标是获取一个位图并返回一个具有最近邻缩放比例的更大比例的位图,因此每个像素变成 2x2 正方形或 3x3 等等。假设 inputBuffer 是由 setOutput 方法确定的输出大小的比例分数。

var pixelateMatrix = gpu.createKernel(function(inputBuffer, width, height, scale) {
var y = Math.floor((this.thread.x / (width[0] * 4)) / scale[0]);
var x = Math.floor((this.thread.x % (width[0] * 4)) / scale[0]);
var remainder = this.thread.x % 4;
return inputBuffer[(x * y) + remainder];
}).setOutput([width * height * 4]);

JSFiddle

Keep in mind it's iterating over a new buffer of the full size output, so I have to find the correct coordinates that will exist in the smaller sourceBuffer based on the current index in the outputBuffer (index is exposed by the lib as this.thread.x).

发生了什么:

这不是制作最近邻高档,而是制作了一个漂亮的小彩虹(上面是小的普通渲染,下面是着色器的结果,在右边你可以看到一些调试日志,其中包含有关输入的统计信息和输出缓冲区):

Result

我做错了什么?

注意:我在这里问了一个相关问题,Is there a simpler (and still performant) way to upscale a canvas render with nearest neighbor resampling?

最佳答案

2018 年 5 月 1 日至 25 日更新

我能够解决大部分问题。有不少

  1. 转换的逻辑是错误的,而且由于某种原因数据被翻转了,所以我翻转了 cols 和 rows 从右下角开始

    var pixelateMatrix = gpu.createKernel(function(inputBuffer, width, height, scale) {
    var size = width[0] * height[0] * 4;
    var current_index = Math.floor((size - this.thread.x)/4);
    var row = Math.floor(current_index / (width[0] * scale[0]) );
    var col = Math.floor((current_index % width[0])/scale[0]);
    var index_old = Math.floor(row * (width[0] / scale[0])) + width[0] - col;
    var remainder = this.thread.x % 4;
    return inputBuffer[index_old * 4 + remainder];

    }).setOutput([width * height * 4]);
  2. 您在 float 中使用宽度和高度,我已将其更改为先计算再缩放

    var smallWidth = Math.floor(window.innerWidth / scale);
    var smallHeight = Math.floor(window.innerHeight / scale);

    var width = smallWidth * scale;
    var height = smallHeight * scale;


    var rt = new THREE.WebGLRenderTarget(smallWidth, smallHeight);
    var frameBuffer = new Uint8Array(smallHeight * smallHeight * 4);
    var outputBuffer = new Uint8ClampedArray(width * height * 4);
  3. Canvas 大小设置为内宽高,需要设置为图片宽高

    context = canvas.getContext('2d');
    canvas.width = width;
    canvas.height = height;

下面是相同的最终 JSFiddle

https://jsfiddle.net/are5Lbw8/6/

结果:

Working Upscale

最终引用代码

var container;
var camera, scene, renderer;
var mouseX = 0;
var mouseY = 0;
var scale = 4;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var smallWidth = Math.floor(window.innerWidth / scale);
var smallHeight = Math.floor(window.innerHeight / scale);

var width = smallWidth * scale;
var height = smallHeight * scale;


var rt = new THREE.WebGLRenderTarget(smallWidth, smallHeight);
var frameBuffer = new Uint8Array(smallHeight * smallHeight * 4);
var outputBuffer = new Uint8ClampedArray(width * height * 4);
var output;
var divisor = 2;
var divisorHalf = divisor / 2;
var negativeDivisorHalf = -1 * divisorHalf;
var canvas;
var context;
var gpu = new GPU();

var pixelateMatrix = gpu.createKernel(function(inputBuffer, width, height, scale) {
/* var y = Math.floor((this.thread.x / (width[0] * 4)) / scale[0]);
var x = Math.floor((this.thread.x % (width[0] * 4)) / scale[0]);
var remainder = this.thread.x % 4;
return inputBuffer[(x * y) + remainder];
*/
var size = width[0] * height[0] * 4;
var current_index = Math.floor((size - this.thread.x)/4);
var row = Math.floor(current_index / (width[0] * scale[0]) );
var col = Math.floor((current_index % width[0])/scale[0]);
var index_old = Math.floor(row * (width[0] / scale[0])) + width[0] - col;
var remainder = this.thread.x % 4;
return inputBuffer[index_old * 4 + remainder];

}).setOutput([width * height * 4]);
console.log(window.innerWidth);
console.log(window.innerHeight);
init();
animate();

function init() {
container = document.createElement('div');
document.body.appendChild(container);
canvas = document.createElement('canvas');
document.body.appendChild(canvas);
context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 100;

// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0xbbbbbb);
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xdddddd);
directionalLight.position.set(0, 0, 1);
scene.add(directionalLight);

// texture
var manager = new THREE.LoadingManager();
manager.onProgress = function(item, loaded, total) {
console.log(item, loaded, total);
};
var texture = new THREE.Texture();
var onProgress = function(xhr) {
if (xhr.lengthComputable) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log(Math.round(percentComplete, 2) + '% downloaded');
}
};
var onError = function(xhr) {};
var imgLoader = new THREE.ImageLoader(manager);
imgLoader.load('https://i.imgur.com/P6158Su.jpg', function(image) {
texture.image = image;
texture.needsUpdate = true;
});

// model
var objLoader = new THREE.OBJLoader(manager);
objLoader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/286022/Bulbasaur.obj', function(object) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
});
object.scale.x = 45;
object.scale.y = 45;
object.scale.z = 45;
object.rotation.y = 3;
object.position.y = -10.5;
scene.add(object);
}, onProgress, onError);
renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: false
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(smallWidth, smallHeight);
container.appendChild(renderer.domElement);
renderer.context.webkitImageSmoothingEnabled = false;
renderer.context.mozImageSmoothingEnabled = false;
renderer.context.imageSmoothingEnabled = false;
document.addEventListener('mousemove', onDocumentMouseMove, false);
window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
windowHalfX = (window.innerWidth / 2) / scale;
windowHalfY = (window.innerHeight / 2) / scale;
camera.aspect = (window.innerWidth / window.innerHeight) / scale;
camera.updateProjectionMatrix();
renderer.setSize(smallWidth, smallHeight);
}

function onDocumentMouseMove(event) {

mouseX = (event.clientX - windowHalfX) / scale;
mouseY = (event.clientY - windowHalfY) / scale;

}

function animate() {
requestAnimationFrame(animate);
render();
}

var flag = 0;

function render() {
camera.position.x += (mouseX - camera.position.x) * .05;
camera.position.y += (-mouseY - camera.position.y) * .05;
camera.lookAt(scene.position);
renderer.render(scene, camera);
renderer.render(scene, camera, rt);
renderer.readRenderTargetPixels(rt, 0, 0, smallWidth, smallHeight, frameBuffer);
//console.time('gpu');
console.log(frameBuffer, [width], [height], [scale]);
var outputBufferRaw = pixelateMatrix(frameBuffer, [width], [height], [scale]);
//console.timeEnd('gpu');
if (flag < 15) {
console.log('source', frameBuffer);
console.log('output', outputBufferRaw);

var count = 0;
for (let i = 0; i < frameBuffer.length; i++) {
if (frameBuffer[i] != 0) {
count++;
}
}
console.log('source buffer length', frameBuffer.length)
console.log('source non zero', count);

var count = 0;
for (let i = 0; i < outputBufferRaw.length; i++) {
if (outputBufferRaw[i] != 0) {
count++;
}
}
console.log('output buffer length', outputBufferRaw.length)
console.log('output non zero', count);
}
outputBuffer = new Uint8ClampedArray(outputBufferRaw);
output = new ImageData(outputBuffer, width, height);
context.putImageData(output, 0, 0);
flag++;
}

原始答案

我已经接近了,但还有两个问题

  1. 图像正在倒转
  2. 有时您的 inputBuffer 大小不是 4 的倍数,这会导致它出现异常。

下面是我使用的代码

var pixelateMatrix = gpu.createKernel(function(inputBuffer, width, height, scale) {
var current_index = Math.floor(this.thread.x/4);
var row = Math.floor(current_index / (width[0] * scale[0]) );
var col = Math.floor((current_index % width[0])/scale[0]);
var index_old = Math.floor(row * (width[0] / scale[0])) + col;
var remainder = this.thread.x % 4;
return inputBuffer[index_old * 4 + remainder];

}).setOutput([width * height * 4]);

下面是 JSFiddle

https://jsfiddle.net/are5Lbw8/

下面是当前输出

Current State

关于javascript - 如何在 JS 中正确编写此着色器函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50430575/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com