在我的着色器中丢失的碎片
问题描述:
我正在试图在Threejs中做一个tilesystem:地面为绿色/水为蓝色。在我的着色器中丢失的碎片
我在PlaneBufferGeometry上使用着色器。
这是我到目前为止有:
相关代码:
- JS:可变
chunk
和功能DoPlaneStuff()
(包括开头)- HTML:顶点和片段着色器
var chunk = {
// number of width and height segments for PlaneBuffer
segments: 32,
// Heightmap: 0 = water, 1 = ground
heightmap: [
[1, 0, 0],
[1, 1, 0],
[1, 0, 1],
],
// size of the plane
size: 40
};
function DoPlaneStuff() {
var uniforms = {
heightmap: {
type: "iv1",
// transform the 2d Array to a simple array
value: chunk.heightmap.reduce((p, c) => p.concat(c), [])
},
hmsize: {
type: "f",
value: chunk.heightmap[0].length
},
coord: {
type: "v2",
value: new THREE.Vector2(-chunk.size/2, -chunk.size/2)
},
size: {
type: "f",
value: chunk.size
}
};
console.info("UNIFORMS GIVEN :", uniforms);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById("v_shader").textContent,
fragmentShader: document.getElementById("f_shader").textContent
});
var plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(chunk.size, chunk.size, chunk.segments, chunk.segments),
shaderMaterial
);
plane.rotation.x = -Math.PI/2;
scene.add(plane);
}
// --------------------- END OF RELEVANT CODE
window.addEventListener("load", Init);
function Init() {
Init3dSpace();
DoPlaneStuff();
Render();
}
var camera_config = {
dist: 50,
angle: (5/8) * (Math.PI/2)
}
var scene, renderer, camera;
function Init3dSpace() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true
});
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth/window.innerHeight,
0.1,
1000
);
this.camera.position.y = camera_config.dist * Math.sin(camera_config.angle);
this.camera.position.x = 0;
this.camera.position.z = 0 + camera_config.dist * Math.cos(camera_config.angle);
this.camera.rotation.x = -camera_config.angle;
var light = new THREE.HemisphereLight(0xffffff, 10);
light.position.set(0, 50, 0);
scene.add(light);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function Render() {
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
<!-- VERTEX SHADER -->
<script id="v_shader" type="x-shader/x-vertex">
// size of the plane
uniform float size;
// coordinates of the geometry
uniform vec2 coord;
// heightmap size (=width and height of the heightmap)
uniform float hmsize;
uniform int heightmap[9];
varying float colorValue;
void main() {
int xIndex = int(floor(
(position.x - coord.x)/(size/hmsize)
));
int yIndex = int(floor(
(-1.0 * position.y - coord.y)/(size/hmsize)
));
// Get the index of the corresponding tile in the array
int index = xIndex + int(hmsize) * yIndex;
// get the value of the tile
colorValue = float(heightmap[index]);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<!-- FRAGMENT SHADER -->
<script id="f_shader" type="x-shader/x-fragment">
varying float colorValue;
void main() {
// default color is something is not expected: RED
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
// IF WATER
if (colorValue == 0.0) {
// BLUE
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}
// IF GROUND
if (colorValue == 1.0) {
// GREEN
gl_FragColor = vec4(0.1, 0.6, 0.0, 1.0);
}
}
</script>
正如你可以看到它几乎工作,但我的这些红线分裂的绿色和蓝色区域,我想不通为什么。 我将这些红色碎片称为“丢失的碎片”,因为它们没有映射到任何碎片,我不明白为什么。
我只能注意到,更大的值chunk.segments
(这是几何的高度和宽度段数),我可以有红线。
我想知道如何在绿色和蓝色区域之间有渐变填充而不是红色。
答
红线由三角形构成,三角形的顶点位于地砖和水砖中的其他顶点。然后,GPU沿着三角形内插colorValue
,产生一个从0到1的平滑渐变,而不是您可能期望的锐步。
有几种解决方案。您可以更改着色器中的条件以根据中点选择颜色:如果colorValue
< 0.5,则输出蓝色,否则为绿色。不过,如果您稍后决定需要更多磁贴类型,那么这样做效果不佳。更好的解决方案是以所有三角形的所有顶点位于一个瓦片中的方式生成几何图形。这将涉及加倍瓦片边界上的顶点。您还可以将flat
插值限定符添加到colorValue
,但难以控制三角形最终将使用哪些顶点的属性。
...我只是注意到,你确实想要一个渐变而不是一个尖锐的步骤。这更容易。您需要将颜色选择代码从片段着色器移动到顶点着色器,并在片段着色器中返回结果插值颜色。
感谢您的回答。 “GPU然后沿着三角插值colorValue”这就是为什么我不能有一个变化的int!但是,我不确定在“更好的解决方案”中理解你的意思,你是否有一些资源可以让我看看? – Apolo