我正在一个Three.js场景中渲染点和线段。如果我将LineBasicMaterial材质用作线条,则The scene会很好:
/**
* constructor for the gl manager
**/
function World() {
this.renderTarget = document.querySelector('#render-target');
this.scene = this.getScene();
this.camera = this.getCamera();
this.renderer = this.getRenderer();
this.controls = this.getControls();
this.masterCounts = null; // {id: nMasters}
this.edges = null; // 2d array where [[master, app, app]]
this.positions = null; // {id: [x,y]}
this.z = 0; // flat z dim
this.loadData();
this.render();
}
World.prototype.getScene = function() {
return new THREE.Scene();
}
World.prototype.getContainerSize = function() {
var elem = this.renderTarget;
return {
w: elem.clientWidth,
h: elem.clientHeight,
}
}
World.prototype.getCamera = function() {
var size = this.getContainerSize();
var camera = new THREE.PerspectiveCamera(75, size.w/size.h, 0.01, 10);
camera.position.set(0.5, 0.5, -0.67);
return camera;
}
World.prototype.getRenderer = function() {
var renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
var size = this.getContainerSize();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(size.w, size.h);
document.querySelector('#render-target').appendChild(renderer.domElement);
return renderer;
}
World.prototype.getControls = function() {
var controls = new THREE.TrackballControls(this.camera, this.renderer.domElement);
controls.zoomSpeed = 0.4;
controls.panSpeed = 0.4;
controls.target.set(0.5, 0.5, 1);
return controls;
}
World.prototype.render = function() {
requestAnimationFrame(this.render.bind(this));
this.renderer.render(this.scene, this.camera);
this.controls.update();
}
World.prototype.getPointScale = function() {
return window.devicePixelRatio * window.innerHeight * 0.00001;
}
World.prototype.loadData = function() {
get('https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/node-positions-twopi.json', function(data) {
this.positions = center(JSON.parse(data));
get('https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/id-to-aggregate-masters.json', function(data) {
this.masterCounts = JSON.parse(data);
get('https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/edges.json', function(data) {
this.edges = JSON.parse(data);
this.addPoints();
this.addEdges();
}.bind(this));
}.bind(this));
}.bind(this));
}
World.prototype.addPoints = function() {
var geometry = new THREE.InstancedBufferGeometry(),
translations = getPointTranslations(this.positions),
colors = getColors(this.positions, this.masterCounts);
geometry.addAttribute('position',
new THREE.BufferAttribute( new Float32Array([0, 0, 0]), true, 3));
geometry.addAttribute('translation',
new THREE.InstancedBufferAttribute(translations, 3, true, 1) );
geometry.addAttribute('target',
new THREE.InstancedBufferAttribute(translations, 3, true, 1) );
geometry.addAttribute('color',
new THREE.InstancedBufferAttribute(colors, 3, true, 1) );
this.points = new THREE.Points(geometry, this.getShaderMaterial());
this.points.frustumCulled = false; // prevent mesh click on drag
this.scene.add(this.points);
}
World.prototype.addEdges = function() {
var indices = [],
positions = [],
idToIndex = {}, // {node id: index in edgePositions}
ids = Object.keys(this.edges);
// flatten edges into [[s,t],[s,t]]
for (var i=0; i<ids.length; i++) {
var idEdges = this.edges[ids[i]];
for (var j=0; j<idEdges.length; j++) {
// here ids[i] is a master node id, idEdges is list of
// apprentice node ids
var masterId = ids[i];
var apprenticeId = idEdges[j];
if (!(masterId in idToIndex)) {
idToIndex[masterId] = positions.length;
positions.push(this.positions[masterId]);
}
if (!(apprenticeId in idToIndex)) {
idToIndex[apprenticeId] = positions.length;
positions.push(this.positions[apprenticeId]);
}
indices = indices.concat([
idToIndex[masterId],
idToIndex[apprenticeId]
]);
}
}
var geometry = new THREE.BufferGeometry(),
translations = new Float32Array(3*positions.length),
iter = 0,
indices = new Uint16Array(indices);
for (var i=0; i<positions.length; i++) {
var e = positions[i];
translations[iter++] = e[0];
translations[iter++] = e[1];
translations[iter++] = this.z;
}
var material = new THREE.LineBasicMaterial({
color: 0xee6559,
opacity: 0.3,
transparent: true,
})
geometry.addAttribute('position',
new THREE.BufferAttribute(translations, 3, true, 1));
geometry.setIndex(new THREE.BufferAttribute(indices, 1, true, 1));
this.lines = new THREE.LineSegments(geometry, material);
this.lines.frustumCulled = false; // prevent mesh click on drag
this.scene.add(this.lines);
}
World.prototype.getShaderMaterial = function() {
return new THREE.RawShaderMaterial({
vertexShader: find('#vertex-shader').textContent,
fragmentShader: find('#fragment-shader').textContent,
uniforms: {
transitionPercent: { type: 'f', value: 0.0 },
pointScale: { type: 'f', value: this.getPointScale(), },
}
});
}
/**
* Helpers
**/
function get(url, handleSuccess, handleErr, handleProgress) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status === 200) {
if (handleSuccess) handleSuccess(xmlhttp.responseText)
} else {
if (handleErr) handleErr(xmlhttp)
}
};
};
xmlhttp.onprogress = function(e) {
if (handleProgress) handleProgress(e);
};
xmlhttp.open('GET', url, true);
xmlhttp.send();
};
function find(querySelector) {
return document.querySelector(querySelector);
}
window.addEventListener('resize', function() {
var size = world.getContainerSize();
world.camera.aspect = size.w / size.h;
world.camera.updateProjectionMatrix();
world.renderer.setSize(size.w, size.h);
})
function center(data) {
var ids = Object.keys(data);
// find the domains of each axis in the data
var p = Number.POSITIVE_INFINITY,
n = Number.NEGATIVE_INFINITY,
domains = {x: {min: p, max: n}, y: {min: p, max: n}};
for (var i=0; i<ids.length; i++) {
vals = data[ids[i]];
if (vals[0] < domains.x.min) domains.x.min = vals[0];
if (vals[0] > domains.x.max) domains.x.max = vals[0];
if (vals[1] < domains.y.min) domains.y.min = vals[1];
if (vals[1] > domains.y.max) domains.y.max = vals[1];
}
// center each axis 0:1
for (var i=0; i<ids.length; i++) {
var d = data[ids[i]];
d[0] = (d[0]-domains.x.min)/(domains.x.max-domains.x.min);
d[1] = (d[1]-domains.y.min)/(domains.y.max-domains.y.min);
}
return data;
}
function getPointTranslations(data) {
var ids = Object.keys(data);
var arr = new Float32Array(ids.length*3),
iter = 0;
for (var i=0; i<ids.length; i++) {
arr[iter++] = data[ids[i]][0];
arr[iter++] = data[ids[i]][1];
arr[iter++] = world.z;
}
return arr;
}
function getColors(data, masterCounts) {
var ids = Object.keys(data);
var maxMasters = 0;
for (var i=0; i<ids.length; i++) {
if (masterCounts[ids[i]] > maxMasters) maxMasters = masterCounts[ids[i]];
}
var colors = [
'#1f77b4', '#86abd7', '#cbcdd3', '#f8dba8',
'#eec570', '#eba055', '#ee6559',
];
var arr = new Float32Array(ids.length * 3),
iter = 0;
for (var i=0; i<ids.length; i++) {
var nMasters = Math.min(colors.length, masterCounts[ids[i]] || 0);
var hex = colors[ Math.floor(colors.length * (nMasters / maxMasters)) ];
var c = hexToRgb(hex);
arr[iter++] = c.r / 255;
arr[iter++] = c.g / 255;
arr[iter++] = c.b / 255;
}
return arr;
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
}
function rgbToHex(r, g, b) {
return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
} : {r: 0, g: 0, b: 0};
}
var world = new World();
html,
body {
width: 100%;
height: 100%;
background: linear-gradient(#efefef, #efefef);
}
body {
margin: 0;
overflow: hidden;
}
div#select-target {
padding: 20px 0;
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
font-family: 'Mallory';
text-indent: 0.01px;
text-overflow: '';
border: none;
padding: 7px 40px 7px 10px;
background-image: url(down-caret.png);
background-position: 90% 50%;
background-size: 15px;
background-repeat: no-repeat;
font-size: 1em;
text-transform: uppercase;
border: 1px solid #c7c7c7;
font-family: arial, sans-serif;
}
#render-container {
text-align: center;
max-height: 100%;
max-width: 100%;
padding-bottom: 20px;
}
#render-target {
margin: 0 auto;
width: 700px;
height: 700px;
}
<div id='render-container'>
<div id='select-target'></div>
<div id='render-target'></div>
</div>
<script src='https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/three.min.js'></script>
<script src='https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/trackball-controls.min.js'></script>
<script src='https://s3.amazonaws.com/duhaime/blog/visualizations/line-segments-network/tweenlite.min.js'></script>
<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float transitionPercent;
uniform vec3 cameraPosition;
uniform float pointScale;
attribute vec3 position;
attribute vec3 translation;
attribute vec3 target;
attribute vec3 color;
varying vec3 vColor;
float scalePointZ(in vec4 pos, in vec3 cameraPosition) {
float zDelta = pow(pos[2] - cameraPosition[2], 2.0);
float delta = pow(zDelta, 0.5);
float scaled = pointScale / delta;
return scaled;
}
void main() {
vec3 t1 = position + translation;
vec3 t2 = position + target;
vec3 pos = mix(t1, t2, clamp(transitionPercent, 0.0, 1.0));
vec4 mvPos = modelViewMatrix * vec4(pos, 1.0);
gl_Position = projectionMatrix * mvPos;
gl_PointSize = 6.0;
vColor = color;
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;
varying vec3 vColor;
void main() {
// make points circular
vec2 coord = gl_PointCoord - vec2(0.5);
if (length(coord) > 0.5) discard;
// set point color
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
</script>
但是,我一直在努力使用原始着色器材料制作线条以实现相同的场景。这是我尝试为原始着色器材料设置线条的方法之一:
World.prototype.addEdges = function() {
var indices = [],
positions = [],
idToIndex = {}, // {node id: index in edgePositions}
ids = Object.keys(this.edges);
// flatten edges into [[s,t],[s,t]]
for (var i=0; i<ids.length; i++) {
var idEdges = this.edges[ids[i]];
for (var j=0; j<idEdges.length; j++) {
// here ids[i] is a master node id, idEdges is list of
// apprentice node ids
var masterId = ids[i];
var apprenticeId = idEdges[j];
if (!(masterId in idToIndex)) {
idToIndex[masterId] = positions.length;
positions.push(this.positions[masterId]);
}
if (!(apprenticeId in idToIndex)) {
idToIndex[apprenticeId] = positions.length;
positions.push(this.positions[apprenticeId]);
}
indices = indices.concat([
idToIndex[masterId],
idToIndex[apprenticeId]
]);
}
}
var geometry = new THREE.BufferGeometry(),
translations = new Float32Array(3*positions.length),
iter = 0,
indices = new Uint16Array(indices);
for (var i=0; i<positions.length; i++) {
var e = positions[i];
translations[iter++] = e[0];
translations[iter++] = e[1];
translations[iter++] = this.z;
}
var material = this.getShaderMaterial().clone();
geometry.addAttribute('position',
new THREE.BufferAttribute(new Float32Array([0, 0, 0]), 3, true, 1));
geometry.addAttribute('translation',
new THREE.BufferAttribute(translations, 3, true, 1));
geometry.addAttribute('target',
new THREE.BufferAttribute(translations, 3, true, 1));
geometry.setIndex(new THREE.BufferAttribute(indices, 1, true, 1));
this.lines = new THREE.LineSegments(geometry, material);
this.lines.frustumCulled = false; // prevent mesh click on drag
this.scene.add(this.lines);
}
但是,这什么也没有呈现。有谁知道我如何使用上面定义的原始着色器材料渲染上面的线条?任何指示或建议都将非常有帮助!
答案 0 :(得分:2)
我认为发生这种情况是因为您在片段着色器中使用了Convolution2D
,尽管您不是在渲染点而是在渲染线。如果我删除以下两行代码,则会呈现您的行:
gl_PointCoord
演示:https://jsfiddle.net/Ldynhxkq/
对于两个基元最好使用不同的着色器程序。