我正在尝试创建位移贴图,但是当涉及到在沙子中的岩石上有位移的大型平面等时,仅一个10x10方形平面就需要一个高质量的位移所需要的1000个段就显得异常缓慢,并产生帧速率问题。我希望该平面的高度为500x500,这将需要50,000个分段,并且每个纹理上都有位移贴图。
作为位移的解决方案,这确实很糟糕,但是我不知道如何制作着色器!
我阅读了着色器,我知道它们是如何工作的,但是我不知道从THREE.js r96开始。特别是r96,因为着色器的属性属于几何体(无论如何,我认为这就是它的工作方式),而不是材质。我挖出的每个教程都使用带有基于GLSL代码的着色器属性的着色器材料。
如何在THREE.js中使用着色器解决位移问题?
代码(对眼睛和/或大脑受损致歉):
<!DOCTYPE html>
<html id="html">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Home</title>
<script src="js/jquery.min.js"></script>
<script src="js/scene.js"></script>
<script src="js/cannon.min.js"></script>
<script src="three.min.js"></script>
<script src="js/THREEx.keyboardstate.js"></script>
<script src="js/controls.js"></script>
<link rel="stylesheet" href="codemirror/lib/codemirror.css">
<link rel="stylesheet" href="codemirror\theme\tomorrow-night-bright.css">
<link rel="stylesheet" href="codemirror\theme\3024-night.css">
<script src="codemirror/lib/codemirror.js"></script>
<script src="codemirror/mode/javascript/javascript.js"></script>
<script type="text/plain" id="vertexshader">
attribute float displacement;
varying vec3 vNormal;
void main() {
vNormal = normal;
// push the displacement into the
// three slots of a 3D vector so
// it can be used in operations
// with other 3D vectors like
// positions and normals
vec3 newPosition = position +
normal * vec3(displacement);
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(newPosition, 1.0);
}
</script>
<script type="text/plain" id="fragmentshader">
varying vec3 vNormal;
void main() {
// calc the dot product and clamp
// 0 -> 1 rather than -1 -> 1
vec3 light = vec3(0.5, 0.2, 1.0);
// ensure it's normalized
light = normalize(light);
// calculate the dot product of
// the light to the vertex normal
float dProd = max(0.0,
dot(vNormal, light));
// feed into our frag colour
gl_FragColor = vec4(dProd, // R
dProd, // G
dProd, // B
1.0); // A
}
</script>
<style>
@import url('https://fonts.googleapis.com/css?family=Titillium+Web');
@import url('https://fonts.googleapis.com/css?family=Quicksand&subset=latin-ext');
::-webkit-scrollbar {
display: none;
}
#container {
margin:0;
border:none;
padding:none;
position:fixed;
overflow:hidden;
height:100%;
width:100%;
}
textarea {
background:black;
color:white;
border:none;
outline:none;
display:none;
}
html{
-ms-overflow-style: -ms-autohiding-scrollbar;
margin:0;
border:none;
padding:none;
overflow:hidden;
overflow-x:hidden;
height:100%;
width:100%;
}
body {
width:100%;
height:100%;
border:none;
margin:0;
}
form {
position:absolute;
bottom:100px;
position:fixed;
display:none;
}
html {
font-family: 'Quicksand', sans-serif;
}
.error {
background-color:red;
color:black;
}
.log {
outline: 1px solid black;
}
.bluetext {
color:blue;
}
#consoleOutputError {
display: none;
}
</style>
</head>
<body>
<div id="container"></div>
<form id="consoleForm" onsubmit="return false;" style="width:30%">
<textarea rows="20" spellcheck="false" cols="70" type="text" id="consoletext"></textarea>
<input type="button" onclick="evaluateTheConsole()" value="Run code">
<input type="button" onclick="document.getElementById('consoleOutputError').innerHTML=''" value="">
</form>
<ul style="color:red;position:absolute;right:10px;top:0px;width:10%" id="consoleOutputError"></ul>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("consoletext",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}), {
lineNumbers: true,
mode: "text/javascript",
theme: "3024-night"
});
function printColor(message,color,bkgrnd="none"){
document.getElementById("consoleOutputError").innerHTML+="<li class='log' style='overflow-wrap: break-word;word-wrap:break-word;white-space:pre-line;padding:2px;list-style-type:none;color:"+color+";background-color:"+bkgrnd+"'>"+message+"</li>";
document.getElementById("html").scrollTop = document.getElementById("html").scrollHeight;
}
(function(){
var oldLog = console.log;
console.log = function (message) {
printColor(message,"black");
oldLog.apply(console, arguments);
};
})();
(function(){
var oldErr = console.error;
console.error = function (message) {
printColor(message,"red");
oldErr.apply(console, arguments);
};
})();
(function(){
var oldWrn = console.warn;
console.warn = function (message) {
printColor(message,"orange");
oldWrn.apply(console, arguments);
};
})();
function evaluateTheConsole(){
try {
eval(editor.getValue());
} catch(err) {
var recent = document.getElementById("consoleOutputError").innerHTML;
printColor(err.message,"red");
document.getElementById("html").scrollTop = document.getElementById("html").scrollHeight;
}
}
// Set the scene size.
const WIDTH = window.innerWidth;
const HEIGHT = window.innerHeight;
// Set some camera attributes.
const VIEW_ANGLE = 45;
const ASPECT = WIDTH / HEIGHT;
const NEAR = 0.1;
const FAR = 10000;
// Get the DOM element to attach to
const container =
document.querySelector('#container');
// Create a WebGL renderer, camera
// and a scene
const renderer = new THREE.WebGLRenderer();
var camera =
new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR
);
const scene = new THREE.Scene();
var stem = "./"
scene.background = new THREE.CubeTextureLoader().load([
stem+"xpos2.png",
stem+"xneg2.png",
stem+"ypos2.png",
stem+"yneg2.png",
stem+"zpos2.png",
stem+"zneg2.png",
])
// Add the camera to the scene.
scene.add(camera);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target = new THREE.Vector3(0, 0, 0);
camera.position.set(0,1,0)
// Start the renderer.
renderer.setSize(WIDTH, HEIGHT);
// Attach the renderer-supplied
// DOM element.
container.appendChild(renderer.domElement);
// create a point light
var pointLight =
new THREE.PointLight(0xFFFFFF);
// set its position
pointLight.position.x = 100;
pointLight.position.y = 100;
pointLight.position.z = 100;
var pointLight2 =
new THREE.PointLight(0xFFFFFF);
// set its position
pointLight2.position.x = -100;
pointLight2.position.y = 100;
pointLight2.position.z = -100;
var setUVs = function(mesh,x,y){
var uvs = mesh.geometry.attributes.uv;
for ( var i = 0; i < uvs.count; i++ ) {
uvs.setXY( i, uvs.getX( i ) * x, uvs.getY( i ) * y );
}
var i = 0;
}
// add to the scene
scene.add(pointLight);
// create the sphere's material
var attributes = {
displacement: {
type: 'f', // a float
value: [] // an empty array
}
};
var vShader = $('#vertexshader');
var fShader = $('#fragmentshader');
// create the material and now
// include the attributes property
var shaderMesh =
new THREE.Mesh(
new THREE.BoxBufferGeometry(1,1,1),
new THREE.ShaderMaterial({
uniforms: attributes,
vertexShader: vShader.text(),
fragmentShader: fShader.text(),
wireframe:true
})
);
// now populate the array of attributes
var verts =
shaderMesh.geometry.vertices;
var values =
shaderMesh.geometry.vertices;
for (var v = 0; v < shaderMesh.geometry.vertices; v++) {
values.push(Math.random() * 30);
}
scene.add(shaderMesh);
var tex = new THREE.TextureLoader().load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg");
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
var geo = new THREE.PlaneBufferGeometry(2, 2);
var mat = new THREE.ShaderMaterial({
uniforms:{
time: {value: 0},
texture1: {value: tex}
},
vertexShader:`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,2.0);
}
`,
fragmentShader: `
uniform float time;
uniform sampler2D texture1;
varying vec2 vUv;
void main() {
vec3 c = vec3(texture2D(texture1, vUv));
float v = time + (vUv.x*0.5 + vUv.y*0.5);
vec2 Uv2 = vec2(c.r + c.b+v, c.g + c.b + v);
vec3 outcolor = vec3(texture2D(texture1, Uv2));
gl_FragColor = vec4( outcolor, 1.0 );
}
`
});
var plane = new THREE.Mesh(geo, mat);
scene.add(plane);
plane.position.y = 15;
shaderMesh.position.y = 10;
var sphereMaterial =
new THREE.MeshPhongMaterial(
{
envMap: scene.background,
map: new THREE.TextureLoader().load("energyrock.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
normalMap: new THREE.TextureLoader().load("energyrocknormal.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementMap: new THREE.TextureLoader().load("energyrockbump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
aoMap: new THREE.TextureLoader().load("energyrockao.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
specularMap: new THREE.TextureLoader().load("energyrockspecular.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
emissiveMap: new THREE.TextureLoader().load("energyrockemissive.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
color:"#ffffff",
emissive:"#bb00bb",
specular:"#000000",
reflectivity:1,
displacementScale:0.03
});
var sphereMaterial2 =
new THREE.MeshPhongMaterial(
{
envMap: scene.background,
map: new THREE.TextureLoader().load("cobblestone.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementMap: new THREE.TextureLoader().load("cobblestonebump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
specularMap: new THREE.TextureLoader().load("cobblestoneSpecular.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
aoMap: new THREE.TextureLoader().load("cobblestoneao.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
normalMap: new THREE.TextureLoader().load("cobblestoneicenormal.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
alphaMap:new THREE.TextureLoader().load("cobblestonealpha.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
//transparent:true,
reflectivity:0.6,
color: "#aaaaff",
specular: "#4444aa",
displacementBias: 0.0,// from original model
displacementScale: 0.05,
});
var brickMaterial =
new THREE.MeshPhongMaterial(
{
envMap: scene.background,
map: new THREE.TextureLoader().load("brick.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementMap: new THREE.TextureLoader().load("brickbump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
bumpMap: new THREE.TextureLoader().load("brickbump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
normalMap: new THREE.TextureLoader().load("bricknormal.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
specularMap: new THREE.TextureLoader().load("brickspecular.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementScale:0.03,
reflectivity:0.5,
color: "#ffaaaa",
specular: "#4444aa"
});
var rockMaterial =
new THREE.MeshPhongMaterial(
{
envMap: scene.background,
map: new THREE.TextureLoader().load("granite.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
normalMap: new THREE.TextureLoader().load("granitenormal.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
bumpMap: new THREE.TextureLoader().load("granitebump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
bumpScale:0,
reflectivity:0.5,
color: "#aaaaff",
specular: "#4444aa"
});
var mudMaterial =
new THREE.MeshPhongMaterial(
{
envMap: scene.background,
map: new THREE.TextureLoader().load("mud.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
normalMap: new THREE.TextureLoader().load("mudnormal.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
bumpMap: new THREE.TextureLoader().load("mudbump.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
aoMap: new THREE.TextureLoader().load("mudao.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementMap: new THREE.TextureLoader().load("muddisplacement.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
specularMap: new THREE.TextureLoader().load("muddisplacement.jpg",function ( texture ) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.offset.set( 0, 0 );texture.repeat.set( 1, 1 )}),
displacementScale:0.015,
reflectivity:0,
color: "#ddccaa",
specular: "#000000",
//specular: "#4444aa"
});
// Set up the sphere vars
const RADIUS = 1;
const SEGMENTS = 16;
const RINGS = 16;
// Create a new mesh with
// sphere geometry - we will cover
// the sphereMaterial next!
var sphere = new THREE.Mesh(
new THREE.PlaneBufferGeometry(
1, 1,100,100),
sphereMaterial2.clone());
var plane2 = new THREE.Mesh(
new THREE.PlaneBufferGeometry(
1, 1,10,10),
sphereMaterial2.clone());
sphere.rotation.x = Math.PI / -2;
plane2.rotation.x = Math.PI / -2;
var rocks = new THREE.Mesh(
new THREE.BoxBufferGeometry(
3, 3, 3,300,300),
rockMaterial.clone());
// Move the Sphere back in Z so we
// can see it.
sphere.position.z = 0;
rocks.position.y = 3;
camera.position.z = -3;
// Finally, add the sphere to the scene.
scene.add(sphere);
//scene.add(plane2);
plane2.position.y = 0.015
scene.add(rocks);
setUVs(sphere,1,1);
setUVs(rocks,3,3);
var g = 0;
var b = 0;
var vueDifference = 20;
var speed = 1;
var wow;
var hColor = 0;
var i=0;
var tty = 0;
var materialtextures = [];
Object.keys(sphere.material).forEach(function(key) {
console.log(key, sphere.material[key]);
if(key=="map"&&sphere.material[key]!=null&&sphere.material[key]!=undefined){
//materialtextures[tty]=sphere.materials[key];
//materialtextures[tty].wrapS = materialtextures[tty].wrapT = THREE.RepeatWrapping;
tty++;
}
});
function announce(expression) {
console.log(expression);
return expression
}
var animationsEnabled = true;
var colorAnimationEnabled = true;
var customAnimationEnabled = true;
var clockStopped = false;
var i = 0;
var iCurrent=0;
function setAnimTimer(time) {
console.log("Paused animation loop at " + time);
i=time;
clockStopped=true;
iCurrent = time;
return time;
}
var customAnimation = function(time){};
var currentColor = 90;
function update () {
if(clockStopped==false){
i++;
if(customAnimationEnabled==true){
customAnimation(i);
}
if(animationsEnabled==true){
mat.uniforms.time.value = i;
renderer.render(scene, camera);
hColor = hColor + speed;
if(Math.round((Math.sin(i/100)+1)*35)==0){
currentColor = Math.floor((Math.random() * 360)+1);
}else if(Math.round((Math.sin(i/100)+1)*50)==100){
currentColor = Math.floor((Math.random() * 360)+1);
};
wow2 = String("hsl(" + currentColor + "," + 100 + "%" + "," + Math.round((Math.sin(i/100)+1)*50) + "%" + ")");
wow = String("hsl(" + currentColor + "," + 100 + "%" + "," + Math.round((Math.sin(i/100)+1)*50) + "%" + ")");
wow3 = String("hsl(" + (Math.sin(i/100)+1)*180 + "," + 100 + "%" + "," + 70 + "%" + ")");
// Draw!
//sphere.rotateY(0.01);
if(colorAnimationEnabled==true){
//sphere.material.emissive = new THREE.Color(wow2);
}
//sphere.rotateX(0.001);
//sphere.rotateX(0.001);
//sphere.material.specular = new THREE.Color(wow);
//
// Schedule the next frame.
}
//sphere.material.displacementScale = (Math.sin(i/100)+1)/60;
controls.update;
renderer.render(scene, camera);
requestAnimationFrame(update);
}else{
i=iCurrent;
}
}
// Schedule the first frame.
requestAnimationFrame(update);
</script>
</body>
</html>