Threejs自定义着色器 - 屏幕撕裂 [英] Threejs Custom Shader - Screen Tearing

查看:1479
本文介绍了Threejs自定义着色器 - 屏幕撕裂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以使用Threejs和Brandon Jone的tilemap方法实现tilemap(在这里找到)我为每个图层使用 THREE.Plane 几何体,并使用以下自定义着色器绘制面部:

So to implement a tilemap using Threejs and Brandon Jone's tilemap method (found here) I am using a THREE.Plane geometry for each layer, and painting the face with the following custom shaders:

顶点着色器:

var tilemapVS = [
    "varying vec2 pixelCoord;",
    "varying vec2 texCoord;",

    "uniform vec2 mapSize;",
    "uniform vec2 inverseTileTextureSize;",
    "uniform float inverseTileSize;",

    "void main(void) {",
    "    pixelCoord = (uv * mapSize);",
    "    texCoord = pixelCoord * inverseTileTextureSize * inverseTileSize;",
    "    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
    "}"
].join("\n");

片段着色器:

var tilemapFS = [
    "varying vec2 pixelCoord;",
    "varying vec2 texCoord;",

    "uniform sampler2D tiles;",
    "uniform sampler2D sprites;",

    "uniform vec2 inverseTileTextureSize;",
    "uniform vec2 inverseSpriteTextureSize;",
    "uniform float tileSize;",
    "uniform int repeatTiles;",

    "void main(void) {",
    "    vec4 tile = texture2D(tiles, texCoord);", //load this pixel of the tilemap
    "    if(tile.x == 1.0 && tile.y == 1.0) { discard; }", //discard if R is 255 and G is 255
    "    vec2 spriteOffset = floor(tile.xy * 256.0) * tileSize;", //generate the offset in the tileset this pixel represents
    "    vec2 spriteCoord = mod(pixelCoord, tileSize);",
    "    vec4 texture = texture2D(sprites, (spriteOffset + spriteCoord) * inverseSpriteTextureSize);",
    "    gl_FragColor = texture;",
    "}"
].join("\n");

每个纹理的设置如下:

//Setup Tilemap
this.tilemap.magFilter = THREE.NearestFilter;
this.tilemap.minFilter = THREE.NearestMipMapNearestFilter;
//tilemap.flipY = false;
if(this.repeat) {
    this.tilemap.wrapS = this.tilemap.wrapT = THREE.RepeatWrapping;
} else {
    this.tilemap.wrapS = this.tilemap.wrapT = THREE.ClampToEdgeWrapping;
}

//Setup Tileset
this.tileset.wrapS = this.tileset.wrapT = THREE.ClampToEdgeWrapping;
this.tileset.flipY = false;
if(this.filtered) {
    this.tileset.magFilter = THREE.LinearFilter;
    this.tileset.minFilter = THREE.LinearMipMapLinearFilter;
} else {
    this.tileset.magFilter = THREE.NearestFilter;
    this.tileset.minFilter = THREE.NearestMipMapNearestFilter;
}

制服是:

//setup shader uniforms
this._uniforms = {
    mapSize: { type: 'v2', value: new THREE.Vector2(this.tilemap.image.width * this.tileSize, this.tilemap.image.height * this.tileSize) },
    inverseSpriteTextureSize: { type: 'v2', value: new THREE.Vector2(1/this.tileset.image.width, 1/this.tileset.image.height) },
    tileSize: { type: 'f', value: this.tileSize },
    inverseTileSize: { type: 'f', value: 1/this.tileSize },

    tiles: { type: 't', value: this.tilemap },
    sprites: { type: 't', value: this.tileset },

    inverseTileTextureSize: { type: 'v2', value: new THREE.Vector2(1/this.tilemap.image.width, 1/this.tilemap.image.height) },
    repeatTiles: { type: 'i', value: this.repeat ? 1 : 0 }
};

实际的几何和网格:

//create the shader material
this._material = new THREE.ShaderMaterial({
    uniforms: this._uniforms,
    vertexShader: tilemapVS,
    fragmentShader: tilemapFS,
    transparent: false
});

this._plane = new THREE.PlaneGeometry(
    this.tilemap.image.width * this.tileSize * this.tileScale,
    this.tilemap.image.height * this.tileSize * this.tileScale
);

this._mesh = new THREE.Mesh(this._plane, this._material);
this._mesh.z = this.zIndex;

this.tileSize 16 this.tileScale 4 。我遇到的问题是,在16x16瓷砖的边缘周围,我会有一些撕裂:

this.tileSize is 16 and this.tileScale is 4. The problem I am having is that around the edges of the 16x16 tiles I get some tearing:

奇怪的是它不是一直发生的,只是沿着y轴移动时很少发生(然而,在我的linux盒子上,问题更严重,并影响x轴)。

The strange part is that it doesn't happen all the time, only sparatically when moving along the y axis (however on my linux box the issue is much worse and effects the x axis as well).

这几乎就像16x16瓷砖在放置时少量关闭我的顶点着色器;但我不确定是什么导致它。感谢任何帮助!

It is almost like the 16x16 tiles are off by a small amount when placed with my vertex shader; but I am not sure what is causing it. Any help is appreciated, thanks!

编辑

这是一张更好的图片撕裂时,它在草地上更明显:

Here is a better image of the tearing, it is more visible on grassy areas:

正如您所看到的那样,它位于16x16平铺边缘(因为它们按 4 )。

As you can see it is along the 16x16 tile edges (since they are scaled by 4).

推荐答案

您正在使用mipmapped纹理,并且您的纹理坐标在切片边缘处不连续:

You are using a mipmapped texture and your texture coordinates are discontinous at the edges of the tiles:

"    vec2 spriteOffset = floor(tile.xy * 256.0) * tileSize;", //generate the offset in the tileset this pixel represents
"    vec2 spriteCoord = mod(pixelCoord, tileSize);",
"    vec4 texture = texture2D(sprites, (spriteOffset + spriteCoord) * inverseSpriteTextureSize);",

地板和mod函数意味着纹理坐标突然在两个像素之间跳跃(如你所愿,为了获得不同的直径) e)。

The floor and mod functions mean that the texture coordinates suddenly jump between two pixels (as you intend, to get a different tile).

然而,纹理查找使用像素之间坐标的变化来确定要使用的mipmap级别,因此在图块边缘它会跳转以使用更小的mipmap并且存在不连续性,因为这是与其他像素不同的mipmap。

However, the texture lookup uses the change in coordinates between pixels to determine what mipmap level to use, so at tile edges it jumps up to use much smaller mipmaps and there is a discontinuity as this is a different mipmap than the rest of the pixels.

最快的方法是不使用mipmap例如以下之一:

The quickest way round this is to not use mipmaps e.g. one of:

texture.minFilter = THREE.LinearFilter;
texture.minFilter = THREE.NearestFilter;

否则,如果您需要mipmap,则需要将第三个参数偏差传递给texture2D。通常将此值设置为0.0,然后当您处于标题边缘时,将此值设置为您想要遍历的mipmap数量的负数,以抵消此效果。

Otherwise if you need mipmaps you'll need to pass the third parameter bias to texture2D. Set this to 0.0 generally and then when you are at the title edges set this to the negative number of the number of mipmaps you want to traverse back up to counteract this effect.

如-11.0将从最低的单像素mipmap到最大为0的全尺寸纹理(2048x2048)的11个mipmap。确切的值需要根据缩放图像大小纹理大小等进行一些计算。

e.g. -11.0 will take you up 11 mipmaps from the lowest single pixel mipmap up to 0 the fullsized texture at 2048x2048. The exact value will need some calculation based on zoom image size textures size etc.

这篇关于Threejs自定义着色器 - 屏幕撕裂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆