使用 three.js 的函数图示器
Function Grapher using three.js
如何以绘制函数的方式实现函数图示器
z=f(x,y) 使用 three.js API。这个程序应该:
以 .1(十分之一)为增量生成介于 -1 和 1 之间的输入值,并使用它来绘制 x、y 和 z 顶点以作为网格的一部分包括在内然后以图形方式显示。
实现鼠标控件,以便可以使用鼠标旋转和缩放映射的函数。
包括一个平面和一个轴助手,为指示原点的函数提供参考点,假设原点为 x=0、y=0 和 z=0。
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.133.1";
import {OrbitControls}
// declare global variables
let scene, camera, cameraControls, renderer, axisHelper;
function init() {
scene = new THREE.Scene;
camera = new THREE.PerspectiveCamera(25, window.innerWidth/window.innerHeight, 1, 1000);
camera.position.set(-30, 50, -7);
const canvas = document.querySelector('#canvasElem');
renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true,
opacity: 0.5, transparent: true, alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
cameraControls.addEventListener("mousemove", renderer);
cameraControls.enablePan = false;
axisHelper = new THREE.AxesHelper;
scene.add(axisHelper);
window.addEventListener("resize", ()=>{
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix()
});
//Constants for use with hyperbolic paraboloid.
//If these are changed, the shape will change! Don't use zero.
const a = 1.0;
const b = 1.0;
/**
* This is the function that will compute z-position from X and Y positions
* to produce a hyperbolic parabaloid.
* @param {*} u
* @param {*} v
* @param {*} w
*/
function ComputeZ(u,v,w) {
x = u * 2 - 1;
y = v * 2 - 1;
w.set(x,y,(x*x)/(a*a) - (y*y)/(b*b));
}
/**
* These is a getter function used to set vertex colors.
* In the version where I code the geometry myself,
* they were set in the geometry generation function.
* @param {*} pointX
* @param {*} pointY
* @param {*} pointZ
* @returns
*/
function getVertexColors(pointX, pointY, pointZ) {
return new THREE.Color(pointX.x*0.5+0.5, pointY.y*0.5+0.5, pointZ.z*0.5+0.5);
}
/**
* The getter function (getVertexColors) one is to make
* the setter function (setVertexColors) more compact.
* @param {*} geometry
*/
//for (var i = 0; i < geometry.faces.length; i++)
//for (i in geometry.faces)
function setVertexColors(geometry) {
for (var i = 0; i < geometry.faces; i++) {
var face = geometry.faces[i];
face.vertexColors = [getVertexColors(geometry.vertices[face.a]),
getVertexColors(geometry.vertices[face.b]),
getVertexColors(geometry.vertices[face.c])];
// face.vertexColors[0] = new THREE.Color("rgb(255,0,0)");
// face.vertexColors[1] = new THREE.Color("rgb(0,255,0)");
// face.vertexColors[2] = new THREE.Color("rgb(0, 0,25)");
// face.vertexColors[3] = new THREE.Color("rgb(0, 0, 0)")
}
}
/**
* This creates a plane grid in the x-y direction, using the specified size and steps.
* @param {number} size
* @param {number} steps
*/
function createPlaneGrid(size, steps) {
// create a grouping for the lines
var group = new THREE.Group();
// create the material for the grid
var material = new THREE.LineBasicMaterial({color:0x000000, transparent:true, opacity:0.25
});
// create the x-axis lines for the grid
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
// create a base class for geometries and add a customed geometry to it
var geometry = new THREE.BufferGeometry();
// create the x-axis custom lines of the grid
const points = [
new THREE.Vector3( f*size, -size*0.5, 0 ),
new THREE.Vector3( f*size, size*0.5, 0 )
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
//create the x-axis line mesh
var axisX = new THREE.Line( geometry, material );
//add the line to the group
group.add( axisX );
}
// create the y-axis lines (geometry) for the grid
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
// create a base class for geometries and add a customed geometry to it
var geometry = new THREE.BufferGeometry();
// create the y-axis custom lines (geometry) of the grid
const points = [
new THREE.Vector3( -size*0.5, f*size, 0 ),
new THREE.Vector3( size*0.5, f*size, 0 ),
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
//create the y-axis line mesh
var axisY = new THREE.Line( geometry, material );
//add the line to the group
group.add( axisY );
}
// return the group when all these calculations are done
return group;
}
// create a parametric geometry
var ParamGeometry = new THREE.ParametricGeometry(ComputeZ, 20, 20);
setVertexColors(ParamGeometry);
// create a material
var ParaMaterial = new THREE.MeshBasicMaterial({color:0xffffff, side:THREE.DoubleSide,
vertexColors: THREE.VertexColors});
//Create models and groups
var ParaMesh = new THREE.Mesh( ParamGeometry, ParaMaterial ); //Create the actual object
scene.add( ParaMesh );
var plane = createPlaneGrid(4,12);
scene.add( plane );
// add directional lighting
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
directionalLight.position.set(100,100,100);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.far = 1000;
scene.add( directionalLight );
}}
function animate(){
requestAnimationFrame(animate);
render();
}
function render(){
// cameraControls.update();
renderer.render(scene, camera);
}
init();
animate();
</script>
图形已绘制,但未应用颜色,使形状变成黑色和白色。
我尝试了不同的方法来为面部涂色,但都没有用。
我需要这方面的帮助。
一个非常粗略的“绘图仪”示例 bended/distorted PlaneGeometry
:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.133.1";
import {
OrbitControls
} from "https://cdn.skypack.dev/three@0.133.1/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 10);
camera.position.set(1, 1.5, 1).setLength(2.5);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x161616);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let grid = new THREE.GridHelper(2, 20, 0xffff00, 0xffff00);
grid.position.y = -0.001;
scene.add(grid, new THREE.AxesHelper(1));
let graphGeom = new THREE.PlaneGeometry(2, 2, 20, 20);
graphGeom.rotateX(Math.PI * -0.5);
let graphMat = new THREE.MeshNormalMaterial({side: THREE.DoubleSide, wireframe: false});
let graph = new THREE.Mesh(graphGeom, graphMat);
// f(x,z)
let pos = graphGeom.attributes.position;
for(let i = 0; i < pos.count; i++){
let x = pos.getX(i);
let z = pos.getZ(i);
pos.setY(i, Math.sin(x * z * Math.PI) * Math.cos(z * z * Math.PI * 0.5) * 0.75);
}
graphGeom.computeVertexNormals();
scene.add(graph);
window.addEventListener("resize", onResize);
renderer.setAnimationLoop(_ => {
renderer.render(scene, camera);
})
function onResize(event) {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
</script>
如何以绘制函数的方式实现函数图示器 z=f(x,y) 使用 three.js API。这个程序应该:
以 .1(十分之一)为增量生成介于 -1 和 1 之间的输入值,并使用它来绘制 x、y 和 z 顶点以作为网格的一部分包括在内然后以图形方式显示。
实现鼠标控件,以便可以使用鼠标旋转和缩放映射的函数。
包括一个平面和一个轴助手,为指示原点的函数提供参考点,假设原点为 x=0、y=0 和 z=0。
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.133.1";
import {OrbitControls}
// declare global variables
let scene, camera, cameraControls, renderer, axisHelper;
function init() {
scene = new THREE.Scene;
camera = new THREE.PerspectiveCamera(25, window.innerWidth/window.innerHeight, 1, 1000);
camera.position.set(-30, 50, -7);
const canvas = document.querySelector('#canvasElem');
renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true,
opacity: 0.5, transparent: true, alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
cameraControls.addEventListener("mousemove", renderer);
cameraControls.enablePan = false;
axisHelper = new THREE.AxesHelper;
scene.add(axisHelper);
window.addEventListener("resize", ()=>{
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix()
});
//Constants for use with hyperbolic paraboloid.
//If these are changed, the shape will change! Don't use zero.
const a = 1.0;
const b = 1.0;
/**
* This is the function that will compute z-position from X and Y positions
* to produce a hyperbolic parabaloid.
* @param {*} u
* @param {*} v
* @param {*} w
*/
function ComputeZ(u,v,w) {
x = u * 2 - 1;
y = v * 2 - 1;
w.set(x,y,(x*x)/(a*a) - (y*y)/(b*b));
}
/**
* These is a getter function used to set vertex colors.
* In the version where I code the geometry myself,
* they were set in the geometry generation function.
* @param {*} pointX
* @param {*} pointY
* @param {*} pointZ
* @returns
*/
function getVertexColors(pointX, pointY, pointZ) {
return new THREE.Color(pointX.x*0.5+0.5, pointY.y*0.5+0.5, pointZ.z*0.5+0.5);
}
/**
* The getter function (getVertexColors) one is to make
* the setter function (setVertexColors) more compact.
* @param {*} geometry
*/
//for (var i = 0; i < geometry.faces.length; i++)
//for (i in geometry.faces)
function setVertexColors(geometry) {
for (var i = 0; i < geometry.faces; i++) {
var face = geometry.faces[i];
face.vertexColors = [getVertexColors(geometry.vertices[face.a]),
getVertexColors(geometry.vertices[face.b]),
getVertexColors(geometry.vertices[face.c])];
// face.vertexColors[0] = new THREE.Color("rgb(255,0,0)");
// face.vertexColors[1] = new THREE.Color("rgb(0,255,0)");
// face.vertexColors[2] = new THREE.Color("rgb(0, 0,25)");
// face.vertexColors[3] = new THREE.Color("rgb(0, 0, 0)")
}
}
/**
* This creates a plane grid in the x-y direction, using the specified size and steps.
* @param {number} size
* @param {number} steps
*/
function createPlaneGrid(size, steps) {
// create a grouping for the lines
var group = new THREE.Group();
// create the material for the grid
var material = new THREE.LineBasicMaterial({color:0x000000, transparent:true, opacity:0.25
});
// create the x-axis lines for the grid
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
// create a base class for geometries and add a customed geometry to it
var geometry = new THREE.BufferGeometry();
// create the x-axis custom lines of the grid
const points = [
new THREE.Vector3( f*size, -size*0.5, 0 ),
new THREE.Vector3( f*size, size*0.5, 0 )
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
//create the x-axis line mesh
var axisX = new THREE.Line( geometry, material );
//add the line to the group
group.add( axisX );
}
// create the y-axis lines (geometry) for the grid
for (var i = 0; i <= steps; i+=1) {
var f = (i/steps)-0.5;
// create a base class for geometries and add a customed geometry to it
var geometry = new THREE.BufferGeometry();
// create the y-axis custom lines (geometry) of the grid
const points = [
new THREE.Vector3( -size*0.5, f*size, 0 ),
new THREE.Vector3( size*0.5, f*size, 0 ),
]
geometry.setFromPoints(points);
geometry.computeVertexNormals();
//create the y-axis line mesh
var axisY = new THREE.Line( geometry, material );
//add the line to the group
group.add( axisY );
}
// return the group when all these calculations are done
return group;
}
// create a parametric geometry
var ParamGeometry = new THREE.ParametricGeometry(ComputeZ, 20, 20);
setVertexColors(ParamGeometry);
// create a material
var ParaMaterial = new THREE.MeshBasicMaterial({color:0xffffff, side:THREE.DoubleSide,
vertexColors: THREE.VertexColors});
//Create models and groups
var ParaMesh = new THREE.Mesh( ParamGeometry, ParaMaterial ); //Create the actual object
scene.add( ParaMesh );
var plane = createPlaneGrid(4,12);
scene.add( plane );
// add directional lighting
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
directionalLight.position.set(100,100,100);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.far = 1000;
scene.add( directionalLight );
}}
function animate(){
requestAnimationFrame(animate);
render();
}
function render(){
// cameraControls.update();
renderer.render(scene, camera);
}
init();
animate();
</script>
图形已绘制,但未应用颜色,使形状变成黑色和白色。 我尝试了不同的方法来为面部涂色,但都没有用。 我需要这方面的帮助。
一个非常粗略的“绘图仪”示例 bended/distorted PlaneGeometry
:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.133.1";
import {
OrbitControls
} from "https://cdn.skypack.dev/three@0.133.1/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 10);
camera.position.set(1, 1.5, 1).setLength(2.5);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x161616);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let grid = new THREE.GridHelper(2, 20, 0xffff00, 0xffff00);
grid.position.y = -0.001;
scene.add(grid, new THREE.AxesHelper(1));
let graphGeom = new THREE.PlaneGeometry(2, 2, 20, 20);
graphGeom.rotateX(Math.PI * -0.5);
let graphMat = new THREE.MeshNormalMaterial({side: THREE.DoubleSide, wireframe: false});
let graph = new THREE.Mesh(graphGeom, graphMat);
// f(x,z)
let pos = graphGeom.attributes.position;
for(let i = 0; i < pos.count; i++){
let x = pos.getX(i);
let z = pos.getZ(i);
pos.setY(i, Math.sin(x * z * Math.PI) * Math.cos(z * z * Math.PI * 0.5) * 0.75);
}
graphGeom.computeVertexNormals();
scene.add(graph);
window.addEventListener("resize", onResize);
renderer.setAnimationLoop(_ => {
renderer.render(scene, camera);
})
function onResize(event) {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
</script>