Load textures from Base64 in Three.js - javascript

I am currently loading textures from URLs but since my back-end code is generating planets I need them to be displayed using Base64.
(I'm playing around with procedural generation so I'd prefer not to save the image and then load it via URL)
Here's the code;
<!DOCTYPE html><html class=''>
<head>
<style>body {
background: black;
text-align: center;
}
</style></head><body>
<script id="vertexShader" type="x-shader/x-vertex">
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main({
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec3 glowColor;
varying float intensity;
void main() {
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}
</script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r63/three.min.js'></script><script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/orbitcontrols.js'></script>
<script>var container, controls, camera, renderer, scene, light,
rotationSpeed = 0.02,
clock = new THREE.Clock(),
WIDTH = window.innerWidth - 30,
HEIGHT = window.innerHeight - 30;
//cam vars
var angle = 45,
aspect = WIDTH / HEIGHT,
near = 0.1,
far = 10000;
//mesh vars
var earthMesh, Atmos, AtmosMat;
container = document.createElement('div');
document.body.appendChild(container);
//cam
camera = new THREE.PerspectiveCamera(angle, aspect, near, far);
camera.position.set(1380, -17, 394);
//scene
scene = new THREE.Scene();
camera.lookAt(scene.position);
//light
light = new THREE.SpotLight(0xFFFFFF, 1, 0, Math.PI / 2, 1);
light.position.set(4000, 4000, 1500);
light.target.position.set (1000, 3800, 1000);
light.castShadow = true;
//light.shadowCameraNear = 1;
//light.shadowCameraFar = 10000;
//light.shadowCameraFov = 50;
scene.add(light);
//EARTH
var earthGeo = new THREE.SphereGeometry (200, 400, 400),
earthMat = new THREE.MeshPhongMaterial();
earthMesh = new THREE.Mesh(earthGeo, earthMat);
earthMesh.position.set(-100, 0, 0);
earthMesh.rotation.y=5;
scene.add(earthMesh);
//diffuse
earthMat.map = THREE.ImageUtils.loadTexture('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/earthmap.jpg');
//bump
earthMat.bumpMap = THREE.ImageUtils.loadTexture('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/bump-map.jpg');
earthMat.bumpScale = 8;
//specular
earthMat.specularMap = THREE.ImageUtils.loadTexture('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/earthspec1k.jpg');
earthMat.specular = new THREE.Color('#2e2e2e');
earthMesh.castShadow = true;
earthMesh.receiveShadow = true;
//Atmosphere
AtmosMat = new THREE.ShaderMaterial({
uniforms:{
"c": { type: "f", value: 0.3 },
"p": { type: "f", value: 5.2},
glowColor: { type: "c", value: new THREE.Color(0x00dbdb)},
viewVector: { type: "v3", value: camera.position}
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
side: THREE.BackSide,
blending: THREE.AdditiveBlending,
transparent: true
});
Atmos = new THREE.Mesh(earthGeo, AtmosMat);
Atmos.position = earthMesh.position;
Atmos.scale.multiplyScalar(1.2);
scene.add(Atmos);
//STARS
var starGeo = new THREE.SphereGeometry (3000, 10, 100),
starMat = new THREE.MeshBasicMaterial();
starMat.map = THREE.ImageUtils.loadTexture('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/star-field.png');
starMat.side = THREE.BackSide;
var starMesh = new THREE.Mesh(starGeo, starMat);
scene.add(starMesh);
//renderer
renderer = new THREE.WebGLRenderer({antialiasing : true});
renderer.setSize(WIDTH, HEIGHT);
container.appendChild(renderer.domElement);
//controls
controls = new THREE.OrbitControls( camera, renderer.domElement);
controls.addEventListener( 'change', render );
function animate(){
requestAnimationFrame(animate);
controls.update();
render();
}
function render(){
var delta = clock.getDelta();
earthMesh.rotation.y += rotationSpeed * delta;
renderer.clear();
renderer.render(scene, camera);
}
animate();
//# sourceURL=pen.js
</script>
</body></html>
I have tried;
image = document.createElement( 'img' );
document.body.appendChild( image );
earthMat.map = new THREE.Texture( image );
image.addEventListener( 'load', function ( event ) { texture.needsUpdate = true; } );
image.src = 'data:image/png;base64,<?php echo $image_data_base64 ?>';
But it doesn't seem to be working correctly.
Any help would be greatly appreciated, thanks.

Turns out I had to do;
earthMat.map = THREE.ImageUtils.loadTexture( image.src );
Instead of;
earthMat.map = new THREE.Texture( image );
new event listener;
image.addEventListener( 'load', function ( event ) {
earthMat.map = THREE.ImageUtils.loadTexture( image.src );
earthMat.needsUpdate = true;
});

Perhaps this does not meet the needs of the original question with the base64 string coming from a PHP script, but in our case the solution was much simpler (THREE.js r130):
const manager = new THREE.LoadingManager()
const texture = new THREE.TextureLoader(manager).load('data:image/png;base64,...')

Related

Three.js “webgl-template.html:69 Uncaught TypeError: Cannot read property 'domElement' of undefined" error

I'm trying to run this code I downloaded from https://www.benzedrine.ch/3D-ODRPP.html
<!DOCTYPE html>
<!-- saved from url=(0056)https://www.benzedrine.ch/vistaprint/webgl-template.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
canvas {
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">info</div>
<script src="/js/three.min.js"></script>
<script src="/js/TrackballControls.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
varying vec3 vWorldPosition;
void main() {
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
vWorldPosition = worldPosition.xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float offset;
uniform float exponent;
varying vec3 vWorldPosition;
void main() {
float h = normalize( vWorldPosition + offset ).y;
gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( max( h , 0.0), exponent ), 0.0 ) ), 1.0 );
}
</script>
<script>
var container, camera, controls, scene, renderer;
var mesh;
function animate() {
requestAnimationFrame(animate);
controls.update();
}
function init() {
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.set(411, 218, 559);
/*
controls = new THREE.OrbitControls( camera );
controls.damping = 1.0;
*/
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.15;
controls.addEventListener('change', render);
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xffffff, 1, 5000);
scene.fog.color.setHSL(0.6, 0, 1);
var geometry;
var material = new THREE.MeshLambertMaterial({ color: 0x1ec876 });
//XXX
var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
hemiLight.color.setHSL(0.6, 1, 0.6);
hemiLight.groundColor.setHSL(0.095, 1, 0.75);
hemiLight.position.set(0, 500, 0);
scene.add(hemiLight);
var dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.color.setHSL(0.1, 1, 0.95);
dirLight.position.set(-1, 1.75, 1);
dirLight.position.multiplyScalar(50);
scene.add(dirLight);
var groundGeo = new THREE.PlaneBufferGeometry(10000, 10000);
var groundMat = new THREE.MeshPhongMaterial({ ambient: 0xffffff, color: 0xffffff, specular: 0x050505 });
groundMat.color.setHSL(0.095, 1, 0.75);
var ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.1;
scene.add(ground);
var vertexShader = document.getElementById('vertexShader').textContent;
var fragmentShader = document.getElementById('fragmentShader').textContent;
var uniforms = {
topColor: { type: "c", value: new THREE.Color(0x0077ff) },
bottomColor: { type: "c", value: new THREE.Color(0xffffff) },
offset: { type: "f", value: 33 },
exponent: { type: "f", value: 0.6 }
}
uniforms.topColor.value.copy(hemiLight.color);
scene.fog.color.copy(uniforms.bottomColor.value);
var skyGeo = new THREE.SphereGeometry(4000, 32, 15);
var skyMat = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, uniforms: uniforms, side: THREE.BackSide });
var sky = new THREE.Mesh(skyGeo, skyMat);
scene.add(sky);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(scene.fog.color, 1);
container = document.getElementById('container');
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}
function render() {
renderer.render(scene, camera);
}
init();
render();
</script>
</body>
</html>
And I got this error
webgl-template.html:69 Uncaught TypeError: Cannot read property 'domElement' of undefined
at init (webgl-template.html:69)
at webgl-template.html:154
this is the code in line 69
controls = new THREE.TrackballControls(camera, renderer.domElement);
Please help me. Thank you all in advance!
The reason it doesn't work is you are using the variables before you actually initialize them, so you get an error that says can not read property of undefined. In javascript the variables will have a value of undefined if you don't initialize them.
var foo;
foo.bar; // You can't do this because foo is undefined
In your example you have to do initialization of the renderer variable before you use it. And since you use the variable scene, you also have to initialize it.
So that's the working code:
function init() {
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xffffff, 1, 5000);
scene.fog.color.setHSL(0.6, 0, 1);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(scene.fog.color, 1);
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.set(411, 218, 559);
controls = new THREE.TrackballControls(camera, renderer.domElement);
...
Remember if you don't have THREE.js locally you have to download it or use the url for the libraries. Like this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>

how to get Sun Rays from the middle of the object in threejs

I have an object where random open particles and others are solid. How do i place a fake sun inside of the object and get Rays through the open particles using threejs. When the object rotate either X,Y Rays should spreed through the open particles ( POST PROCESSING ) ?
SAMPLE VIDEO
My Object three.Js code is bellow
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 10, 10000);
camera.position.z = 200;
scene = new THREE.Scene();
hemiLight = new THREE.HemisphereLight( 0x0000ff, 0x00ff00, 0.6 );
scene.add(hemiLight);
var geometry = new THREE.DodecahedronGeometry(80, 0);
var material = new THREE.MeshPhongMaterial({
color: 0xffffff,
specular: 0xffffff,
shininess: 1,
shading: THREE.FlatShading,
polygonOffset: true,
polygonOffsetFactor: 1,
wireframe:true
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
var wireframe = new THREE.LineSegments( geo, mat );
mesh.add( wireframe );
//outer frame end
//inner world like object start
var sphere_material = [
new THREE.MeshLambertMaterial( { color: 0xffff00, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { transparent: true, opacity: 0 } )
];
var sphere_geometry = new THREE.OctahedronGeometry( 60, 3 );
// assign material to each face
for( var i = 0; i < sphere_geometry.faces.length; i++ ) {
sphere_geometry.faces[ i ].materialIndex = THREE.Math.randInt( 0, 1 );
}
sphere_geometry.sortFacesByMaterialIndex();
var sphere_mesh = new THREE.Mesh( sphere_geometry, sphere_material );
sphere_mesh.position.set(0, 0, 0)
mesh.add(sphere_mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
renderer.gammaInput = true;
renderer.gammaOutput = true;
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.003;
mesh.rotation.y += 0.003;
renderer.render(scene, camera);
}
DEMO
You're looking for volumetric lights. Check this code example below or maybe better look at it on jsfiddle (to see it in full dimensions not overplayed by console log).
It's based on this article. I slightly tweaked your code and updated the colors as well as some values and the code. It still needs some playing (e.g. number of faces, light color, shader properties etc.) but hopefully will help you. It all really depends how you wish to make it look like.
THREE.VolumetericLightShader = {
uniforms: {
tDiffuse: {value:null},
lightPosition: {value: new THREE.Vector2(0.5, 0.5)},
exposure: {value: 0.18},
decay: {value: 0.95},
density: {value: 0.8},
weight: {value: 0.4},
samples: {value: 50}
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"varying vec2 vUv;",
"uniform sampler2D tDiffuse;",
"uniform vec2 lightPosition;",
"uniform float exposure;",
"uniform float decay;",
"uniform float density;",
"uniform float weight;",
"uniform int samples;",
"const int MAX_SAMPLES = 100;",
"void main()",
"{",
"vec2 texCoord = vUv;",
"vec2 deltaTextCoord = texCoord - lightPosition;",
"deltaTextCoord *= 1.0 / float(samples) * density;",
"vec4 color = texture2D(tDiffuse, texCoord);",
"float illuminationDecay = 1.0;",
"for(int i=0; i < MAX_SAMPLES; i++)",
"{",
"if(i == samples){",
"break;",
"}",
"texCoord -= deltaTextCoord;",
"vec4 sample = texture2D(tDiffuse, texCoord);",
"sample *= illuminationDecay * weight;",
"color += sample;",
"illuminationDecay *= decay;",
"}",
"gl_FragColor = color * exposure;",
"}"
].join("\n")
};
THREE.AdditiveBlendingShader = {
uniforms: {
tDiffuse: { value:null },
tAdd: { value:null }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform sampler2D tAdd;",
"varying vec2 vUv;",
"void main() {",
"vec4 color = texture2D( tDiffuse, vUv );",
"vec4 add = texture2D( tAdd, vUv );",
"gl_FragColor = color + add;",
"}"
].join("\n")
};
THREE.PassThroughShader = {
uniforms: {
tDiffuse: { value: null }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
"gl_FragColor = texture2D( tDiffuse, vec2( vUv.x, vUv.y ) );",
"}"
].join( "\n" )
};
(function(){
var scene, camera, renderer, composer, box, pointLight,
occlusionComposer, occlusionRenderTarget, occlusionBox, lightSphere,
volumetericLightShaderUniforms,
DEFAULT_LAYER = 0,
OCCLUSION_LAYER = 1,
renderScale = 0.5,
angle = 0,
sphere_mesh,
mesh;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
function setupScene(){
var ambientLight,
geometry,
material;
ambientLight = new THREE.AmbientLight(0x2c3e50);
scene.add(ambientLight);
pointLight = new THREE.PointLight(0xddddff);
scene.add(pointLight);
geometry = new THREE.SphereBufferGeometry( 1, 32, 32 );
material = new THREE.MeshBasicMaterial( { color: 0x99ddff } );
lightSphere = new THREE.Mesh( geometry, material );
lightSphere.layers.set( OCCLUSION_LAYER );
scene.add( lightSphere );
camera.position.z = 6;
}
function addFragmentedSphere(){
var geometry = new THREE.DodecahedronGeometry(2.9, 0);
var material = new THREE.MeshPhongMaterial({
color: 0x000000,
specular: 0xffffff,
shininess: 1,
flatShading: THREE.FlatShading,
polygonOffset: true,
polygonOffsetFactor: 1,
wireframe:true
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
//outer frame end
//inner world like object start
var sphere_material = [
new THREE.MeshLambertMaterial( { color: 0xffff00, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { transparent: true, opacity: 0 } )
];
var sphere_geometry = new THREE.OctahedronGeometry( 2.7, 4 );
// assign material to each face
for( var i = 0; i < sphere_geometry.faces.length; i++ ) {
sphere_geometry.faces[ i ].materialIndex = THREE.Math.randInt( 0, 1 );
}
sphere_geometry.sortFacesByMaterialIndex();
sphere_mesh = new THREE.Mesh( sphere_geometry, sphere_material );
sphere_mesh.position.set(0, 0, 0)
mesh.add(sphere_mesh);
sphere_mesh.layers.set( OCCLUSION_LAYER );
}
function setupPostprocessing(){
var pass;
occlusionRenderTarget = new THREE.WebGLRenderTarget( window.innerWidth * renderScale, window.innerHeight * renderScale );
occlusionComposer = new THREE.EffectComposer( renderer, occlusionRenderTarget);
occlusionComposer.addPass( new THREE.RenderPass( scene, camera ) );
pass = new THREE.ShaderPass( THREE.VolumetericLightShader );
pass.needsSwap = false;
occlusionComposer.addPass( pass );
volumetericLightShaderUniforms = pass.uniforms;
volumetericLightShaderUniforms.exposure.value = 0.5;
volumetericLightShaderUniforms.decay.value = 0.96;
volumetericLightShaderUniforms.density.value = 0.95;
volumetericLightShaderUniforms.weight.value = 0.59;
volumetericLightShaderUniforms.samples.value = 100;
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );
pass = new THREE.ShaderPass( THREE.AdditiveBlendingShader );
pass.uniforms.tAdd.value = occlusionRenderTarget.texture;
composer.addPass( pass );
pass.renderToScreen = true;
}
function onFrame(){
requestAnimationFrame( onFrame );
update();
render();
}
function update(){
mesh.rotation.x += 0.003;
mesh.rotation.y += 0.003;
}
function render(){
camera.layers.set(OCCLUSION_LAYER);
renderer.setClearColor(0x000000);
occlusionComposer.render();
camera.layers.set(DEFAULT_LAYER);
renderer.setClearColor(0x090611);
composer.render();
}
function addRenderTargetImage(){
var material,
mesh,
folder;
material = new THREE.ShaderMaterial( THREE.PassThroughShader );
material.uniforms.tDiffuse.value = occlusionRenderTarget.texture;
mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), material );
composer.passes[1].scene.add( mesh );
mesh.visible = false;
}
window.addEventListener( 'resize', function(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
var pixelRatio = renderer.getPixelRatio(),
newWidth = Math.floor( window.innerWidth / pixelRatio ) || 1,
newHeight = Math.floor( window.innerHeight / pixelRatio ) || 1;
composer.setSize( newWidth, newHeight );
occlusionComposer.setSize( newWidth * renderScale, newHeight * renderScale );
}, false );
setupScene();
setupPostprocessing();
addFragmentedSphere();//
addRenderTargetImage();
onFrame();
}())
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
<script src="https://abberg.github.io/lib/shaders/CopyShader.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/EffectComposer.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/RenderPass.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/ShaderPass.js"></script>

three.js memory management shader material

I'm trying to create textual labels. I need to use shader material to better control the label during rendering.
I've noticed that the memory keeps increasing even though I clean up old labels.
I've created a jsfiddle example that is not unlike: https://threejs.org/examples/#webgl_test_memory
The following code uses a canvas object to generate a texture, which contains the text to be depicted as a label:
Please be careful, these computations are heavy and make the tab quite unresponsive.
var container;
var camera, scene, renderer;
var labels;
var canvas;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 200;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
labels = new THREE.Object3D();
canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// get text metrics
var fontface = 'Arial';
var fontSize = 60;
context.font = fontSize + "px " + fontface;
var width = context.measureText(text).width;
// add text
var text = 'abcdef';
canvas.width = width;
canvas.height = fontSize*1.3;
context.textAlign = "center";
context.font = fontSize + "px " + fontface;
context.fillStyle = "white";
context.fillText(text, canvas.width / 2, canvas.height / 2);
}
function createLabels() {
for(var i = 0; i < 10000 ; i++) {
createTextMesh();
}
scene.add( labels );
}
function createTextMesh() {
// canvas contents will be used for a texture
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var uniforms = {
text: {
type: 't',
value: texture
}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertex-shader' ).textContent,
fragmentShader: document.getElementById( 'fragment-shader' ).textContent
} );
var geometry = new THREE.PlaneBufferGeometry(15, 15);
var label = new THREE.Mesh( geometry, material );
labels.add(label);
}
function clearLabels() {
for(var i = 0; i < labels.children.length; i++) {
var label = labels.children[i];
if(label.material.uniforms) {
label.material.uniforms.text.value.dispose();
}
label.material.dispose();
label.geometry.dispose();
labels.remove(label);
}
scene.remove( labels );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
// build GL objects
createLabels();
renderer.render( scene, camera );
// clean up
clearLabels();
}
body {
margin:0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
<script id="fragment-shader" type="x-shader/x-fragment">
uniform sampler2D text;
varying vec2 vUv;
void main() {
vec4 finalColor = texture2D(text, vUv);
gl_FragColor = finalColor;
}
</script>
<script id="vertex-shader" type="x-shader/x-fragment">
varying vec2 vUv;
void main() {
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<canvas></canvas>
You can use's chromes dev tools to evaluate the perceptual memory utilisation increase.
I'd recommend using something like Window's own task manager to see the memory increase.
You can decrease the label creation speed, although this will naturally mean that it will take longer for the tab do run out of memory.
Am I doing the resource clean-up wrongly?
Cheers
Hi try out following code
as I have added time gap in animate function which calls render function.
there is one variable fps currently 24, you can change it.
<!DOCTYPE html>
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>three.js - shader material memory leak</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background:#fff;
padding:0;
margin:0;
overflow:hidden;
}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.js"></script>
<script id="fragment-shader" type="x-shader/x-fragment">
uniform sampler2D text;
varying vec2 vUv;
void main() {
vec4 finalColor = texture2D(text, vUv);
gl_FragColor = finalColor;
}
</script>
<script id="vertex-shader" type="x-shader/x-fragment">
varying vec2 vUv;
void main() {
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script>
var container,
camera, scene, renderer,
labels,
canvas,
lastTime, fps = 24;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 200;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
labels = new THREE.Object3D();
canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// get text metrics
var fontface = 'Arial';
var fontSize = 60;
context.font = fontSize + "px " + fontface;
var width = context.measureText(text).width;
// add text
var text = 'abcdef';
canvas.width = width;
canvas.height = fontSize*1.3;
context.textAlign = "center";
context.font = fontSize + "px " + fontface;
context.fillStyle = "white";
context.fillText(text, canvas.width / 2, canvas.height / 2);
}
function createLabels() {
for(var i = 0; i < 10000 ; i++) {
createTextMesh();
}
scene.add( labels );
}
function createTextMesh() {
// canvas contents will be used for a texture
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var uniforms = {
text: {
type: 't',
value: texture
}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertex-shader' ).textContent,
fragmentShader: document.getElementById( 'fragment-shader' ).textContent
} );
var geometry = new THREE.PlaneBufferGeometry(15, 15);
var label = new THREE.Mesh( geometry, material );
labels.add(label);
}
function clearLabels() {
for(var i = 0; i < labels.children.length; i++) {
var label = labels.children[i];
if(label.material.uniforms) {
label.material.uniforms.text.value.dispose();
}
label.material.dispose();
label.geometry.dispose();
labels.remove(label);
}
scene.remove( labels );
}
// Add time gap render will call with 24 fps. or you can slow down speed to check.
function animate() {
var curTime = new Date();
if(lastTime === undefined || (Math.round(curTime - lastTime)/1000) >= fps)
{
render();
lastTime = curTime;
}
requestAnimationFrame( animate );
}
function render() {
// build GL objects
createLabels();
renderer.render( scene, camera );
// clean up
clearLabels();
}
</script>
<div>
<canvas width="1920" height="974" style="width: 1920px; height: 974px;"></canvas>
</div>
</body>
</html>
Don't create a new mesh 1000 times every tick, pool them.
Don't create a 1000 geometries that are all the same plane. This is probably the biggest culprit here. Create just one, ever, and pass it to meshes.
Textures, im not so sure about that one. I think you're not supposed to create a new texture from the canvas context, create once and update canvas.
This will go away with the changes, but it's good to note that for performance you'd want to avoid creating that uniforms object in the loop as well.
EDIT
You do create a lot of stuff. Width of the label comes in as 263, and if three's console log is to be trusted, it's resized to 256x64. So you end up with 480mb of data, although, with the alpha channel it could be 600mb. I assume that your demo never even gets to the disposal part. It just crashed the browser on my end.
Three also complains about the textures being NPOT, so it attempts to write out ten thousand console logs.

How applicate Sobel filter or Frei-Chen Filter only on the images?

I testing Three.js with an example that need to visualize layer of images like this:
When i use 'THREE.EdgeShader' or 'THREE.EdgeShader2' i obtain the filter of images The next image show the results obtained:
My problem arises because the scene is also modified. So, I need apply the filter only for the images. Additionally, I require that after applying the filter, all pixels resulting in black be transparent. Could you help me ?
The code is :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Webgl - postprocessing</title>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel=stylesheet href="css/base.css"/>
</head>
<body>
<script src="js/three.min.js"></script>
<script src="js/Detector.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.WindowResize.js"></script>
<script src="js/libs/stats.min.js"></script>
<script type='text/javascript' src='js/libs/dat.gui.min.js'></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/shaders/EdgeShader.js"></script>
<script src="js/shaders/EdgeShader2.js"></script>
<script src="js/shaders/BokehShader2.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<!-- fragment shader a.k.a. pixel shader -->
<script id="fragmentShader" type="x-shader/x-vertex">
uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}
</script>
<div id="Test3D" style="position: absolute; left:0px; top:0px"></div>
<script>
var myImage;
var container, scene, camera, renderer, controls, stats;
var composer, object, light, View_Angle;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
var Screen_Width, Screen_Height;
var edgeEffect, edgeEffect2;
var gui, parameters;
initScene();
animateScene();
function setScene() {
// Create the scene
scene = new THREE.Scene();
}
function setContainer(renderer) {
container = document.getElementById( 'Test3D' ).appendChild( renderer.domElement );
}
function renderer() {
if ( Detector.webgl )
renderer = new THREE.WebGLRenderer( ); //{antialias:true} );
else
renderer = new THREE.CanvasRenderer();
renderer.setSize(Screen_Width, Screen_Height);
setContainer(renderer);
}
function setCamera(x, y, z) {
// Get the size of the inner window (content area) to create a full size renderer
Screen_Width = window.innerWidth, Screen_Height = window.innerHeight;
View_Angle = 45, ASPECT = Screen_Width / Screen_Height, NEAR = 0.1, FAR = 20000;
// Create the camera
camera = new THREE.PerspectiveCamera( View_Angle, ASPECT, NEAR, FAR);
camera.position.set(x,y,z);
scene.add(camera);
camera.lookAt(scene.position);
}
function setEvents() {
THREEx.WindowResize(renderer, camera);
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
}
function setOrbitControls() {
controls = new THREE.OrbitControls( camera, renderer.domElement );
}
function setStats() {
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
}
function setFloor(image) {
var floorTexture = new THREE.ImageUtils.loadTexture( image );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 1, 1 );
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = 5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
}
function setSkyBox_FOG(isFog, color) {
var skyBoxGeometry = new THREE.BoxGeometry( 10000, 10000, 10000 );
var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: color, side: THREE.BackSide } );
var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
scene.add(skyBox);
if (isFog) {
scene.fog = new THREE.FogExp2( color, 0.00025 );
}
}
function setAmbientLight(color) {
light = new THREE.AmbientLight( color );
scene.add( light );
}
function initScene() {
setScene() //scene = new THREE.Scene();
setCamera(500, 0, 600);
renderer();
setEvents();
setOrbitControls();
setStats() // STATS
setSkyBox_FOG(false, 0xB0C4DE); // SKYBOX/FOG
CreateImages();
PostProcessing();
setAmbientLight(0xeeeeee);
initGUI(); // GUI
}
function createMaterial() {
/*
var materialCameraPosition = camera.position.clone();
var material = new THREE.ShaderMaterial(
{
uniforms: {
"c": { type: "f", value: 128.0 },
"p": { type: "f", value: 3 },
glowColor: { type: "c", value: new THREE.Color(0x84ccff) },
viewVector: { type: "v3", value: materialCameraPosition }
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
//shading: THREE.FlatShading,
//side: THREE.FrontSide,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending,
transparent: true,
//opacity: 0.5,
depthWrite: false
});
*/
var material = new THREE.MeshPhongMaterial({
shading: THREE.FlatShading,
side: THREE.DoubleSide
});
return material;
}
function createImage(x, y, imageName, posX) {
var material = createMaterial();
var geometry = new THREE.PlaneBufferGeometry( x, y );
material.map = THREE.ImageUtils.loadTexture(imageName);
var mesh = new THREE.Mesh( geometry, material );
mesh.position.z = posX;
scene.add( mesh );
return mesh;
}
function CreateImages() {
for ( var i = 0; i < 5; i ++ ) {
createImage(512,246, 'beach.jpg', i*30 + 100);
}
}
function PostProcessing() {
// basic renderer that renders the scene, and uses the
// effectCopy shader to output the image to the defined
// rendertarget.
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass(scene, camera) );
edgeEffect = new THREE.ShaderPass( THREE.EdgeShader );
edgeEffect.uniforms[ 'aspect' ].value.set(window.innerWidth, window.innerHeight);
composer.addPass( edgeEffect );
edgeEffect2 = new THREE.ShaderPass( THREE.EdgeShader2 );
edgeEffect2.uniforms[ 'aspect' ].value.set(window.innerWidth, window.innerHeight);
composer.addPass( edgeEffect2 );
var effectCopy = new THREE.ShaderPass( THREE.CopyShader);
effectCopy.renderToScreen = true;
composer.addPass( effectCopy );
window.addEventListener( 'resize', onWindowResize, false );
initializeParameters(false, false, false, false);
//http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
/*
Filters.threshold = function(pixels, threshold) {
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
var v = (0.2126*r + 0.7152*g + 0.0722*b >= threshold) ? 255 : 0;
d[i] = d[i+1] = d[i+2] = v
}
return pixels;
};
var grayscale = Filters.filterImage(Filter.grayscale, image);
// Note that ImageData values are clamped between 0 and 255, so we need
// to use a Float32Array for the gradient values because they
// range between -255 and 255.
var vertical = Filters.convoluteFloat32(grayscale,
[ -1, 0, 1,
-2, 0, 2,
-1, 0, 1 ]);
var horizontal = Filters.convoluteFloat32(grayscale,
[ -1, -2, -1,
0, 0, 0,
1, 2, 1 ]);
var final_image = Filters.createImageData(vertical.width, vertical.height);
for (var i=0; i<final_image.data.length; i+=4) {
// make the vertical gradient red
var v = Math.abs(vertical.data[i]);
final_image.data[i] = v;
// make the horizontal gradient green
var h = Math.abs(horizontal.data[i]);
final_image.data[i+1] = h;
// and mix in some blue for aesthetics
final_image.data[i+2] = (v+h)/4;
final_image.data[i+3] = 255; // opaque alpha
}
*/
}
function initializeParameters(EdgeDetection, EdgeDetection2) {
edgeEffect.enabled = EdgeDetection;
edgeEffect2.enabled = EdgeDetection2;
}
function initGUI() {
gui = new dat.GUI({
height : 5 * 32 - 1
});
parameters = {
EdgeDetection: false, // Edge Detection (Frei-Chen Filter)
EdgeDetection2: false, // Edge Detection (Sobel Filter)
EdgeAspect: 512,
Threshold: 0.5,
reset: function() {
parameters.EdgeDetection = false;
parameters.EdgeDetection2 = false;
parameters.EdgeAspect = 512;
parameters.Threshold = 0.5;
initializeParameters(false, false, false, false);
}
};
var filters = gui.addFolder('Filters');
filters.add( parameters, 'EdgeDetection').name('Frei-Chen Filter').listen().onChange(
function(value) {
edgeEffect.enabled = value;
});
filters.add( parameters, 'EdgeDetection2').name('Sobel Filter').listen().onChange(
function(value) {
edgeEffect2.enabled = value;
});
filters.open();
var edgeAspect = gui.add( parameters, "EdgeAspect", 128, 2048 ).listen().onChange(
function(value) {
//
edgeEffect.uniforms.aspect.value = new THREE.Vector2(value, value);
});
var threshold = gui.add( parameters, "Threshold", 0, 1, 0.001 ).listen().onChange(
function(value) {
//
});
gui.add( parameters, 'reset' ).name("Reset Parameters");
gui.open();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
edgeEffect.uniforms[ 'aspect' ].value.set(window.innerWidth, window.innerHeight);
edgeEffect2.uniforms[ 'aspect' ].value.set(window.innerWidth, window.innerHeight);
}
function update() {
controls.update();
stats.update();
}
function animateScene() {
// render using requestAnimationFrame
requestAnimationFrame( animateScene );
renderer.autoClear = false;
renderer.clear();
composer.render();
update();
}
</script>
</body>
</html>

Shaders doesn't work in Three.js app

I wrote a program with Three.js. I try to use shaders but, unfortunately, the app does not work. I tried to customized a application from threejs.org. This is the code:
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="ecma/three.js"></script>
<script type="text/javascript" src="ecma/jquery-1.9.0.js"></script>
<script type="text/javascript" src="ecma/OrbitControls.js"></script>
<style>
body{ margin: 0; overflow: hidden; }
</style>
</head>
<body>
<div id="WebGL-output">
</div>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fragment_shader1" type="x-shader/x-fragment">
uniform vec2 resolution;
uniform float time;
varying vec2 vUv;
void main(void)
{
vec2 p = -1.0 + 2.0 * vUv;
float a = time*100.0;
float d,e,f,g=1.0/40.0,h,i,r,q;
e=400.0*(p.x*0.5+0.5);
f=400.0*(p.y*0.5+0.5);
i=200.0+sin(e*g+a/150.0)*20.0;
d=200.0+cos(f*g/2.0)*18.0+cos(e*g)*7.0;
r=sqrt(pow(i-e,2.0)+pow(d-f,2.0));
q=f/r;
e=(r*cos(q))-a/2.0;f=(r*sin(q))-cos(a/2.0);
d=sin(e*g)*176.0+sin(e*g)*164.0+r;
h=((f+d)+a/2.0)*g;
i=cos(h+r*p.x/1.3)*(e+e+a)+cos(q*g*6.0)*(r+h/3.0);
h=sin(f*g)*144.0-sin(e*g)*212.0*p.x;
h=(h+(f-e)*q+sin(r-(a+h)/7.0)*10.0+i/4.0)*g;
i+=cos(h*2.3*sin(a/350.0-q))*184.0*sin(q-(r*4.3+a/12.0)*g)+tan(r*g+h)*184.0*cos(r*g+h);
i=mod(i/5.6,256.0)/64.0;
if(i<0.0) i+=4.0;
if(i>=2.0) i=4.0-i;
d=r/350.0;
d+=sin(d*d*8.0)*0.52;
f=(sin(a*g)+1.0)/2.0;
gl_FragColor=vec4(vec3(f*i/1.6,i/2.0+d/13.0,i)*d*p.x+vec3(i/1.3+d/8.0,i/2.0+d/18.0,i)*d*(1.0-p.x),0.0);
}
</script>
<script type="text/javascript">
$(function () {
var W = window.innerWidth, H = window.innerHeight;
var plane, planeGeom, planeMat;
var sphere, sphereGeom, shadedMat;
var scene, camera, renderer;
var clock;
var orbitControls;
var uniforms1;
init();
makeLights();
makeFloor();
makeSphere();
render();
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, W / H, 0.1, 1000);
camera.position.set(-40, 30, 0);
camera.lookAt(new THREE.Vector3(0,0,0));
renderer = new THREE.WebGLRenderer();
renderer.setClearColorHex(0xEEEEEE);
renderer.setSize(W, H);
renderer.shadowMapEnabled = true;
orbitControls = new THREE.OrbitControls(camera);
orbitControls.autoRotate = false;
clock = new THREE.Clock();
}
function makeLights(){
makeAmbientLight();
makeSpotLight();
}
function makeAmbientLight(){
ambiColor = 0x141414;
ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
}
function makeSpotLight(){
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( -40, 60, -10 );
spotLight.castShadow = true;
scene.add( spotLight );
}
function makeFloor(){
planeGeom = new THREE.PlaneGeometry(100,100);
planeMat = new THREE.MeshLambertMaterial();
var planeTex = THREE.ImageUtils.loadTexture("picim/checkerboard.jpg");
planeTex.wrapS = planeTex.wrapT = THREE.RepeatWrapping;
planeTex.repeat.set(50, 50);
planeMat.map = planeTex;
planeMat.side = THREE.DoubleSide;
plane = new THREE.Mesh(planeGeom, planeMat);
plane.rotation.x=-0.5*Math.PI;
plane.position.set(15, 0, 0);
plane.receiveShadow = true;
scene.add(plane);
}
function makeSphere(){
sphereGeom = new THREE.SphereGeometry(5,20,20);
uniforms1 = {
time: { type: "f", value: 1.0 },
resolution: { type: "v2", value: new THREE.Vector2() }
};
shadedMat = new THREE.ShaderMaterial({uniforms: uniforms1,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragment_shader1').textContent,
});
sphere = new THREE.Mesh(sphereGeom, shadedMat);
sphere.position.set(0, 10, 0);
sphere.castShadow = true;
scene.add(sphere);
}
function render(){
var delta = clock.getDelta();
orbitControls.update(delta);
sphere.rotation.y += 0.02;
requestAnimationFrame(render);
renderer.render(scene, camera);
}
$("#WebGL-output").append(renderer.domElement);
});
</script>
</body>
</html>
The app must display the floor and a sphere, on which the shaders are used. But the app shows a bright white sphere. Could you help me to find the error ?
Thanks,
Ee
If you are seeing a white sphere with these crazy shader, then it means that the shader works no?
If you are asking why is that... shader (and it's quite a shader) rendering white, instead of all that math, it's doubtful that anyone can help you with the presented code.
you can add something like gl_FragColor.xyz=cos(gl_FragColor.xyz)*.5+.5; and you'll see if that white actually has a bigger meaning.

Categories

Resources