Putting image on a sphere using ShaderMaterial in three.js - three.js

I tried to put image on a sphere by using ShaderMaterial in three.js. But I only get a black sphere by my code below. What is the problem?
main.js
(function(){
var width = window.innerWidth;
var height = window.innerHeight;
//scene
var scene = new THREE.Scene();
//geo
var geometry = new THREE.SphereGeometry( 5, 60, 40 );
geometry.scale( -1, 1, 1);
//mesh
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load('images/image.jpeg');
var uniforms = {
color: { type: "3v", value: new THREE.Vector3(0.3, 1.0, 1.0) },
texture: { type: "t", value: 0, texture: texture },
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
//camera
var camera = new THREE.PerspectiveCamera(75, width / height, 1, 1000);
camera.position.set(0,0,30);
camera.lookAt(mesh.position);
//render
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor(0xffffff);
document.getElementById('stage').appendChild(renderer.domElement);
renderer.render(scene,camera);
function render(){
requestAnimationFrame(render);
renderer.render(scene,camera);
}
render();
})();
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<body>
<div id="stage"></div>
<script src="lib/three.min.js"></script>
<script src="lib/OrbitControls.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
uniform vec3 color;
uniform sampler2D texture;
varying vec2 vUv;
void main(){
vec4 samplerColor = texture2D(texture, vUv);
gl_FragColor = vec4(1.0) * samplerColor;
}
</script>
<script src="main.js"></script>
</body>
</html>
When I tried to use MeshBasicMaterial like below, it is OK. So I guess it is not a problem of asynchronous loading..
var material = new THREE.MeshBasicMaterial( {
map: texture
} );
Please help me

Related

THREE.JS: How to rotate object around its centre in vertex shader?

I have a simple THREE.Points object. Have to rotate it around its Y centre axis via shader (green line), as well as general object position. I have commented it by now, so it doesn't affect the rotation issue.
Now it has the wrong "orbital" rotation. Pretty sure that I need to play with tPos. Have tried without any success.
var stats, scene, renderer;
var camera, cameraControl, mesh;
if( !init() ) animate();
function init(){
renderer = new THREE.WebGLRenderer({
antialias : true,
preserveDrawingBuffer : true
});
renderer.setClearColor( 0xBBBBBB, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById('container').appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(0, 0, 5);
scene.add(camera);
var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array( [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0
] );
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.ShaderMaterial( {
side : THREE.DoubleSide,
uniforms: {
parentPosition: { value: new THREE.Vector3(0.0, 0.0, 0.0) },
rotation: { type: "f", value: 0.0 },
texture: { value: new THREE.TextureLoader().load( "assets/blob.png" ) }
},
vertexShader: document.getElementById( "vertexshader" ).textContent,
fragmentShader: document.getElementById( "fragmentshader" ).textContent,
alphaTest: 0.9
} );
mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);
}
function animate() {
//mesh.material.uniforms.parentPosition.value.x += 0.01;
mesh.material.uniforms.rotation.value += 0.01;
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
body{ margin: 0px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<!doctype html>
<html>
<head>
<title>three.js template</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script type="x-shader/x-vertex" id="vertexshader">
uniform vec3 parentPosition;
uniform float rotation;
varying vec4 vColor;
varying mat4 vPosition;
void main() {
mat4 tPos = mat4(vec4(1.0,0.0,0.0,0.0),
vec4(0.0,1.0,0.0,0.0),
vec4(0.0,0.0,1.0,0.0),
vec4(0,0,0,1.0));
mat4 rYPos = mat4(vec4(cos(rotation),0.0,sin(rotation),0.0),
vec4(0.0,1.0,0.0,0.0),
vec4(-sin(rotation),0.0,cos(rotation),0.0),
vec4(0.0,0.0,0.0,1.0));
vPosition = tPos * rYPos;
vColor = vec4(1.0, 0.0, 1.0, 1.0);
vec3 newPosition = position + parentPosition;
vec4 mvPosition = modelViewMatrix * vPosition * vec4( newPosition, 1.0 );
gl_PointSize = 32.0 * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform sampler2D texture;
varying vec4 vColor;
varying mat4 vPosition;
void main() {
gl_FragColor = vColor;
gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
}
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
mat4 tPos = mat4(vec4(1.0,0.0,0.0,0.0),
vec4(0.0,1.0,0.0,0.0),
vec4(0.0,0.0,1.0,0.0),
vec4(0,0,0,1.0));
mat4 rYPos = mat4(vec4(cos(rotation),0.0,sin(rotation),0.0),
vec4(0.0,1.0,0.0,0.0),
vec4(-sin(rotation),0.0,cos(rotation),0.0),
vec4(0.0,0.0,0.0,1.0));
vPosition = tPos * rYPos;
vColor = vec4(1.0, 0.0, 1.0, 1.0);
vec3 newPosition = position + parentPosition;
vec4 mvPosition = modelViewMatrix * vPosition * vec4( newPosition, 1.0 );
Can someone help me?
It's my fault. The code works fine. Have to set all Z values to 0.0.
var vertices = new Float32Array( [
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0
] );

How to display object on browser three js from blender?

I have this object in blender, i want to display it on three js but the result is so different and can't able to change the view, How can i change the view with mouse?
Here's my full code, i'm newbie and this is my first time using three.js and blender:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - loaders - ColladaLoader2</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body{
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="WebGL-output">
</div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="colladaloader.js"></script>
<script>
function init() {
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
var axes = new THREE.AxisHelper(20);
scene.add(axes);
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshBasicMaterial({color: 0xcccccc});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
scene.add(plane);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 0, 4);
var loader = new THREE.ColladaLoader();
loader.load("MAC12.dae", function (result) {
scene.add(result.scene);
});
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
};
window.onload = init;
</script>
</body>
</html>

Adding Three.js to a LitElement Class

I'm learning how to integrate Three.js to Polymer's Lit-Element. My current problem is that I need refer to a div element to append Three's Renderer element. Here's how it is done usually:
box = document.getElementById("box")
box.appendChild(renderer.domElement)
Unfortunately, I am not able find how to refer from the constructor()/firstUpdate() to the div declared in the render function. How would you do that?
Here's my best result for now:
Renderer element off target
Here's the code to get this result:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<box-test></box-test>
<script type="module" src="src/components/box-test.js" crossorigin></script>
</body>
</html>
Javascript:
import { LitElement, html } from '#polymer/lit-element';
import * as THREE from 'three/build/three.module';
class BoxTest extends LitElement {
constructor() {
super();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(300, 300);
//box = document.getElementById("box");
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var animate = function () {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
}
render() {
return html`
<style>
#box { border: 1px solid red; height: 310px; width: 310px;}
</style>
<section>
The webgl animation must be in the red box
<div id="box">
</div>
</section>
`
}
}
window.customElements.define('box-test', BoxTest);
Any suggestion will be welcomed.
In your constructor function you can keep variables to use in other functions later by set it to this
this.renderer = renderer
Then in firstUpdated function you can do
firstUpdated () {
let box = this.shadowRoot.getElementById('box')
box.appendChild(this.renderer.domElement)
}
Example Code:
<script type='module'>
import { LitElement, html } from '#polymer/lit-element'
import * as THREE from 'three/build/three.module'
class BoxTest extends LitElement {
constructor () {
super()
var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000)
var geometry = new THREE.BoxGeometry(1, 1, 1)
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true })
var cube = new THREE.Mesh(geometry, material)
scene.add(cube)
camera.position.z = 5
;(function animate () {
requestAnimationFrame(animate)
cube.rotation.x += 0.01
cube.rotation.y += 0.01
renderer.render(scene, camera)
}())
var renderer = new THREE.WebGLRenderer()
renderer.setSize(300, 300)
this.renderer = renderer
}
firstUpdated () {
let box = this.shadowRoot.getElementById('box')
box.appendChild(this.renderer.domElement)
}
render () {
return html`
<style>
#box { border: 1px solid red; height: 310px; width: 310px;}
</style>
<section>
The webgl animation must be in the red box
<div id="box"></div>
</section>
`
}
}
window.customElements.define('box-test', BoxTest)
</script>

Basic scene breaks transitioning from three.js v87 to v88 or v89

Does anyone know why the following breaks as three.min.js is updated from r87 to r88 or r89? This is just using fundamentals from three.js-related training webistes. With r87 or earlier, a cube is displayed. With r88 or r89, there's just a black canvas. I have tried this with Chrome, Firefox, & IE.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo</title>
<script src="jquery-3.3.1.min.js"></script>
<script src="three.min.js"></script>
<script>
var renderer = null, scene = null, camera = null;
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
$(document).ready(function() {
var canvas = document.getElementById("webglcanvas");
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
renderer.setSize(canvas.width, canvas.height);
camera = new THREE.PerspectiveCamera(60, canvas.width / canvas.height, 0.1, 400);
camera.position.set(60, 100, 100);
scene.add(camera);
camera.lookAt({"x": 50.0, "y": 50.0, "z": 50.0});
var light = new THREE.PointLight(0xffff00);
light.position.set(50, 50, 50);
scene.add(light);
var geometry = new THREE.BoxGeometry(50, 50, 50);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
render();
});
</script>
</head>
<body>
<canvas id="webglcanvas" width="480" height="300"></canvas>
</body>
</html>
The following is not valid three.js code, and never has been valid three.js code:
camera.lookAt( { "x": 50.0, "y": 50.0, "z": 50.0 } );
Use
camera.lookAt( 50, 50, 50 );
or
camera.lookAt( myVector3 );
three.js r.90

How to correctly set the camera

I am having trouble knowing how the set the camera. What i have tried is using a example and just changing the OBJLoader url to my file
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - OBJLoader + MTLLoader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display:block;
}
#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
</style>
</head>
<body>
<div id="info">
three.js - OBJLoader + MTLLoader
</div>
<script src="../build/three.js"></script>
<script src="js/loaders/DDSLoader.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
render();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = -25;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
//directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load( '140018_2.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.load( '140018_2.obj', function ( object ) {
object.position.set(0, 0, 0);
console.log("loaded");
scene.add( object );
});
});
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableZoom = true;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>
It somewhat works. When i load the page i dont see anything. But when i zoom out, the object 'flies in' from the top left corner of my screen. All i really need is just for the object to be centered on the screen.
How are you guys achieving this? The OBJ is not created by me and its settings (height, width etc etc) can vary.
The fact that you say "the object 'flies in' from the top left corner" suggests to me that your .OBJ is not centred, as in, when it was exported from the 3D editor, it was not centred.
It will need to be centred in the 3D authoring program and exported again.

Resources