Threejs自定义着色器 - 屏幕撕裂

时间:2012-11-07 13:53:46

标签: javascript shader webgl three.js textures

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

顶点着色器:

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");

Fragment Shader:

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.tileSize16this.tileScale4。我遇到的问题是,在16x16瓷砖的边缘周围,我会有一些撕裂:

screen tearing

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

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

修改

这是一个更好的撕裂图像,它在草地上更明显:

screen tearing 2

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

2 个答案:

答案 0 :(得分:4)

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

"    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);",

floor和mod函数意味着纹理坐标突然在两个像素之间跳跃(如您所愿,以获得不同的图块)。

但是,纹理查找使用像素之间坐标的变化来确定要使用的mipmap级别,因此在图块边缘它会跳起来使用更小的mipmap并且存在不连续性,因为这是一个不同于其余部分的mipmap像素。

最快的方法是不使用mipmap,例如其中之一:

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

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

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

答案 1 :(得分:0)

我怀疑你是因为相机位置导致sprite uv mapping错误而导致这些问题。基本上,你是在你应该渲染的精灵上的精灵表中显示相邻的精灵。

这一行特别是: `vec2 spriteOffset = floor(tile.xy * 256.0)* tileSize;“

似乎可能导致抵消问题。您可以通过用明亮的颜色填充精灵来查看是否弹出来进行更好的检查。