Display wireframe and solid color - three.js

Is it possible to display the wireframe of the object and also the solid color of its faces on the same object? I found a way using a clone of the object and assign different materials e.g
var geometry = new THREE.PlaneGeometry(plane.width, plane.height,width - 1, height - 1);
var materialWireframe = new THREE.MeshPhongMaterial({color:"red",wireframe:true});
var materialSolid = new THREE.MeshPhongMaterial({color:"green",wireframe:false});
var plane = new THREE.Mesh(geometry, materialWireframe );
var plane1 = plane.clone();
plane1.material = materialSolid ;
plane1.material.needsUpdate = true;
any thoughts?

To render both a model and its wireframe, you can use a pattern like this one:
// mesh
var material = new THREE.MeshPhongMaterial( {
color: 0xff0000,
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh )
// wireframe
var geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xffffff } );
var wireframe = new THREE.LineSegments( geo, mat );
mesh.add( wireframe );
The use of polygonOffset will help prevent z-fighting between the mesh material and the wireframe line. Consequently, the wireframe will look a lot better.
three.js r.126

This can also be achieved with WireframeGeometry:
https://threejs.org/docs/#api/en/geometries/WireframeGeometry.
(and give plane and line the same position, you can also play with opacity see the docs).
let geometryWithFillAndWireFrame = () => {
let geometry = new THREE.PlaneGeometry(250, 250, 10, 10);
let material = new THREE.MeshBasicMaterial( { color: 0xd3d3d3} );
let plane = new THREE.Mesh(geometry, material);
scene.add(plane);
let wireframe = new THREE.WireframeGeometry( geometry );
let line = new THREE.LineSegments( wireframe );
line.material.color.setHex(0x000000);
scene.add(line);
};

To do that, a possibility is to use a GLSL fragment shader that changes the fragment color when the fragment is near one edge of the triangle. Here is the GLSL shader that I am using. As input, it takes the barycentric coordinates of the fragment in the triangle, and an edge mask that selects for each edge whether it should be drawn or not. (rem: I had to use it with the compatibility profile for backward compatibility reasons, if you do not want to do that, it can easily be adapted):
(fragment source)
#version 150 compatibility
flat in float diffuse;
flat in float specular;
flat in vec3 edge_mask;
in vec2 bary;
uniform float mesh_width = 1.0;
uniform vec3 mesh_color = vec3(0.0, 0.0, 0.0);
uniform bool lighting = true;
out vec4 frag_color;
float edge_factor(){
vec3 bary3 = vec3(bary.x, bary.y, 1.0-bary.x-bary.y);
vec3 d = fwidth(bary3);
vec3 a3 = smoothstep(vec3(0.0,0.0,0.0), d*mesh_width, bary3);
a3 = vec3(1.0, 1.0, 1.0) - edge_mask + edge_mask*a3;
return min(min(a3.x, a3.y), a3.z);
}
void main() {
float s = (lighting && gl_FrontFacing) ? 1.0 : -1.0;
vec4 Kdiff = gl_FrontFacing ?
gl_FrontMaterial.diffuse : gl_BackMaterial.diffuse;
float sdiffuse = s * diffuse;
vec4 result = vec4(0.1, 0.1, 0.1, 1.0);
if(sdiffuse > 0.0) {
result += sdiffuse*Kdiff +
specular*gl_FrontMaterial.specular;
}
frag_color = (mesh_width != 0.0) ?
mix(vec4(mesh_color,1.0),result,edge_factor()) :
result;
}

To avoid cloning my object I used a pattern like that :
var mat_wireframe = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
var mat_lambert = new THREE.MeshLambertMaterial({color: 0xffffff, shading: THREE.FlatShading});
var meshmaterials = [ mat_wireframe, mat_lambert ];
and then applied it to my mesh like that :
var myMesh = THREE.SceneUtils.createMultiMaterialObject( mesh_geometry, meshmaterials );
scene.add( myMesh ) ;
I hope it could help...

Related

Colors in THREE.WebGLRenderTarget with alpha channel are darker than expected

I'm trying to render some graphics with transparency into a WebGLRenderTarget. The rendered image is then used as texture for a second material.
I have an issue with alpha blending. The color that I obtain when alpha=0.5 is darker than expected.
The image below shows the issue:
Circle on top is what I expect. This is obtained with an HTML DIV with rounder corners and opacity=0.5
Circle on bottom is what I obtain with with a shader that renders the circle inside a texture.
I think that I'm missing something!
Part of the code is reported below. You can find the complete code in the following jsbin: https://jsbin.com/zukoyaziqe/1/edit?html,js,output
Thank you for your help!!
Shader:
const texFrag = `
varying vec2 vUv;
void main() {
vec2 center = vec2(0.5, 0.2);
float d = length(vUv - center);
if (d < 0.1) {
gl_FragColor = vec4(1.0,0.0,1.0,0.5);
}
else {
discard;
}
}
`;
Texture:
const makeTexture = (renderer, width, height) => {
const target = new THREE.WebGLRenderTarget(width, height, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, type: THREE.FloatType});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(90, 1, 0.1, 100000);
const geometry = new THREE.PlaneGeometry(2, 2);
const material = new THREE.ShaderMaterial({
transparent : true,
vertexShader : simpleVert,
fragmentShader : texFrag,
});
const mesh = new THREE.Mesh(geometry, material);
camera.position.set(0, 0, 1);
scene.add(camera);
scene.add(mesh);
renderer.render(scene, camera, target, true);
return target.texture;
}
Main view:
const renderer = new THREE.WebGLRenderer({canvas});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(90, 1, 0.1, 100000);
const geometry = new THREE.PlaneGeometry( 2, 2 );
const material = new THREE.MeshBasicMaterial({
transparent : true,
map : makeTexture(renderer, canvas.width, canvas.height)
});
const mesh = new THREE.Mesh(geometry, material);
First of all, in the example you linked, your main function is called twice, so there are two CSS circles stacked on top of each other, resulting in a less transparent circle.
Then, you're drawing a circle with color (1,0,1,0.5) on a blank render target, which, using the default blend mode (SRC_ALPHA, ONE_MINUS_SRC_ALPHA), results in (0.5,0,0.5,0.5) color, which is then used as a texture. If you want the original color in your texture, you should disable alpha blending or use a different blend mode. Simply setting transparent to false inside makeTexture does the trick.

Shader material glow bug when pan,zoom in three.js

I create a virtual earth like this with this code:
function earthView(){
if (!scene){
main(); // create three js basic code(camera, renderer etc.)
}
// create the geometry sphere stars
var geometry = new THREE.SphereBufferGeometry(6371000000, 36, 36)
// create the material, using a texture of startfield
var material = new THREE.MeshBasicMaterial()
material.map = THREE.ImageUtils.loadTexture('images/earthView/ESO_-_Milky_Way.jpg')
material.side = THREE.BackSide
// create the mesh based on geometry and material
var mesh = new THREE.Mesh(geometry, material)
mesh.position.set(0,0,-6371000)
scene.add(mesh)
/*
var geometry = new THREE.SphereGeometry(5000,10,10);
var material = new THREE.MeshBasicMaterial({color:"0xff0000"});
var mesh_test = new THREE.Mesh(geometry,material);
scene.add(mesh_test);*/
//earth
var geometry = new THREE.SphereBufferGeometry(6371000, 36, 36)
var material = new THREE.MeshPhongMaterial()
var earthMesh = new THREE.Mesh(geometry, material)
earthMesh.position.set(0,0,-6371000)
earthMesh.rotation.set(Math.PI/2,Math.PI/2,0)
earthMesh.rotation.y -=22.87*Math.PI/180//rightturn ^
earthMesh.rotation.x +=49.02*Math.PI/180//rightturn ^
helper = new THREE.EdgesHelper( earthMesh );
helper.material.color.set( 0xffffff );
material.map = THREE.ImageUtils.loadTexture('images/earthView/earthmap1k.jpg')
material.bumpMap = THREE.ImageUtils.loadTexture('images/earthView/earthbump1k.jpg')
material.bumpScale = 100
material.specularMap = THREE.ImageUtils.loadTexture('images/earthView/earthspec1k.jpg')
scene.add(earthMesh);
scene.add( helper );
//atmosphere
var geometry = new THREE.SphereBufferGeometry(9371000, 36, 36)
var material = new createAtmosphereMaterial()
material.uniforms.glowColor.value.set(0x00b3ff)
material.uniforms.coeficient.value = 0.02
material.uniforms.power.value = 2.5
material.side = THREE.DoubleSide
var earthAtmo = new THREE.Mesh(geometry, material)
earthAtmo.position.set(0,0,-6371000)
scene.add(earthAtmo);
/**
* from http://stemkoski.blogspot.fr/2013/07/shaders-in-threejs-glow-and- halo.html
* #return {[type]} [description]
*/
function createAtmosphereMaterial(){
var vertexShader = [
'varying vec3 vNormal;',
'void main(){',
' // compute intensity',
' vNormal = normalize( normalMatrix * normal );',
' // set gl_Position',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}',
].join('\n')
var fragmentShader = [
'uniform float coeficient;',
'uniform float power;',
'uniform vec3 glowColor;',
'varying vec3 vNormal;',
'void main(){',
' float intensity = pow( coeficient - dot(vNormal, vec3(0.0, 0.0, 1.0)), power );',
' gl_FragColor = vec4( glowColor * intensity, 1.0 );',
'}',
].join('\n')
// create custom material from the shader code above
// that is within specially labeled script tags
var material = new THREE.ShaderMaterial({
uniforms: {
coeficient : {
type : "f",
value : 1.0
},
power : {
type : "f",
value : 2
},
glowColor : {
type : "c",
value : new THREE.Color('blue')
},
},
vertexShader : vertexShader,
fragmentShader : fragmentShader,
side : THREE.BackSide,
blending : THREE.AdditiveBlending,
transparent : true,
depthWrite : false,
});
return material
}
}
in previus question, I had problem with the renderer because i create the virtual earth in real scale (1px = 1m). I overcame this error by setting the logarithmicDepthBuffer: true when defining the renderer.
Now the problem is that the atmosphere (glow shader material) has a bug when panning or zooming in the webgl - container which is already been stated and here is an example to solve this problem.
The question is: how can i change my code to overcome this bug?(I suppose there is something to add in the render function but i just can't get it to work).
Hint1: this bug is only happening when setting the logarithmicDepthBuffer: true. Else i get a false rendering because of the large scale object i am using.
Image1:render option logarithmicDepthBuffer: false,no bug, only false rendering.
Image2:render option logarithmicDepthBuffer: true,no bug, if not zoom or pan.
Image3:render option logarithmicDepthBuffer: true, when zoom in the area of the applied shader material seams to became smaller.
Image4:render option logarithmicDepthBuffer: true,when pan the area of the applied shader material seams not to follow or understand the pan.
Hint2: the area that shader material is renderable seams to became bigger when zooming out and smaller when zooming in.
Update: As a see now the problem is taking place when i add the star sphere. If i dont add the star sphere then everything works correct.. Any thoughts why this is happening??

How to apply a texture to part of a face?

I want to apply a texture to only a specified portion of the face of a cube - not the whole face. I want to do this multiple times to the same face. For example, I want to put texture1.jpg on the top right corner of the face, texture2.jpg on the lower right corner, texture3.jpg on the upper left corner, and texture4.jpg in the middle.
I want to be able to do this without creating new meshes for each portion.
I am experiencing performance issues related to having to create a new mesh for each portion of texture, and if I could just do it by somehow placing "decals" on a single mesh, the whole thing would run smoother.
You can combine textures at custom THREE.ShaderMAterial:
<script id="cubeFragmentShader" type="x-shader/x-fragment">
uniform vec2 aMin;
uniform vec2 aMax;
varying vec2 vUv;
uniform sampler2D texture;
uniform sampler2D texture2;
void main() {
vec2 position = vUv;
if (vUv.x<aMin.x || vUv.x>aMax.x || vUv.y<aMin.y || vUv.y>aMax.y) {
gl_FragColor = texture2D( texture2, position );
} else {
position -= aMin;
position.x /= aMax.x - aMin.x;
position.y /= aMax.y - aMin.y;
gl_FragColor = texture2D( texture, position );
}
}
</script>
http://jsfiddle.net/fj2r9b55/
I drew multiple images on a canvas in different spots and then exported the canvas as a URL, which I added as a textured material to a Mesh:
var scene = new THREE.Scene();
var sceneWidth = window.innerWidth;
var sceneHeight = window.innerHeight;
var camera = new THREE.PerspectiveCamera(45, sceneWidth/sceneHeight, 0.1, 3000);
var controls = new THREE.OrbitControls(camera);
var raycaster = new THREE.Raycaster();
var renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearColor( 0xcccccc, 1);
renderer.setSize(sceneWidth, sceneHeight);
document.getElementById("content").appendChild(renderer.domElement);
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 900;
var ctx = canvas.getContext("2d");
var imgs = [];
imgs.push(loadImage('http://uobot.com/demo/1.jpg', main));
imgs.push(loadImage('http://uobot.com/demo/2.jpg', main));
imgs.push(loadImage('http://uobot.com/demo/3.jpg', main));
var imagesLoaded = 0;
function main() {
imagesLoaded ++;
if(imagesLoaded == imgs.length) {
for(var i in imgs) {
ctx.drawImage(imgs[i], 0, i*100);
}
var dataUrl = canvas.toDataURL();
var texture = THREE.ImageUtils.loadTexture(dataUrl);
texture.needsUpdate = true;
texture.minFilter = THREE.LinearFilter;
var material = new THREE.MeshPhongMaterial( {
map: texture,
specular: 0x050505,
shininess: 0
});
var object = new THREE.Mesh(new THREE.PlaneGeometry(10, 60, 1, 1), material);
scene.add(object);
}
}
function loadImage(src, onload) {
var img = new Image();
img.onload = onload;
img.src = src;
return img;
}

How to map texture on a custom non square quad in THREE JS

playing around with ThreeJS, i encoutered a classic problem of non square quad texturing :
http://www.xyzw.us/~cass/qcoord/
Problem is ThreeJS only let you set texture coordinates with a vec2 (for what i know ..)
And after spending hours on this, and finally found a working solution, i wanted to share it with the community, and maybe get some better ideas ?
So here is the code:
First, the javascript to make my Quad using three JS:
var planeGeom = new THREE.Geometry();
planeGeom.vertices.push(new THREE.Vector3(0, 0, 10));
planeGeom.vertices.push(new THREE.Vector3(10, 0, 10));
planeGeom.vertices.push(new THREE.Vector3(20, 0,0));
planeGeom.vertices.push(new THREE.Vector3(-10, 0, 0));
//create the 2 faces , maybe i should play with CW or CCW order... ;)
planeGeom.faces.push(new THREE.Face3(0,1,3));
planeGeom.faces.push(new THREE.Face3(1,2,3));
//Compute widths ratio
var topWidth = Math.abs(Plane.TR.x - Plane.TL.x);
var bottomWidth = Math.abs(Plane.BR.x - Plane.BL.x);
var ratio = topWidth / bottomWidth;
//create UV's as barely explained in the link above (www.xyzw.us)
var UVS = [
new THREE.Vector2(0, ratio),
new THREE.Vector2(0, 0),
new THREE.Vector2(1.0, 0),
new THREE.Vector2(ratio, ratio)
];
//faceVertexUvs[materialID] [face index] [vertex index among face]
planeGeom.faceVertexUvs[0][0] = [UVS[0],UVS[1],UVS[3]];
planeGeom.faceVertexUvs[0][1] = [UVS[1],UVS[2],UVS[3]];
//load the image
var checkerTexture = THREE.ImageUtils.loadTexture('./resources/images/checker_largeColor.gif');
//Now create custom shader parts
customUniforms =
{
uSampler: { type: "t", value: checkerTexture },
};
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: customUniforms,
vertexShader: document.getElementById( 'vertexShader').textContent,
fragmentShader: document.getElementById( 'fragmentShader').textContent,
side: THREE.DoubleSide
} );
//create the mesh with the custom geometry and material
var planeMesh = new THREE.Mesh(planeGeom, customMaterial);
//add the object to the threeJS scene
this.m_Scene.add(planeMesh);
and now the custom shader code:
Vertex shader:
varying vec4 textureCoord;
void main()
{
//here i reCreate the Vec4 i would have liked to have in threeJS
textureCoord = vec4(uv,0.0, 1.0);
if(uv.y != 0.0)
{
textureCoord.w *= (uv.y);
}
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
and the fragment shader:
uniform sampler2D uSampler;
varying vec4 textureCoord;
void main()
{
gl_FragColor = texture2D(uSampler, vec2(textureCoord.x/textureCoord.w, textureCoord.y/textureCoord.w));
}
voilaaa. I hope it could help some, or maybe myself in a few years... ;)

three.js: BufferGeometry with texture coordinates

I could not found example of THREE.BufferGeometry with texture coordinates. Is it supposed to be used for textured mesh? I can't get it to work. Here is my test code:
var quad_vertices =
[
-30.0, 30.0, 0.0,
30.0, 30.0, 0.0,
30.0, -30.0, 0.0,
-30.0, -30.0, 0.0
];
var quad_uvs =
[
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
];
var quad_indices =
[
0, 2, 1, 0, 3, 2
];
var geometry = new THREE.BufferGeometry();
geometry.attributes =
{
position:
{
itemSize: 3,
array: new Float32Array(3 * 4)
},
uv:
{
itemSize: 2,
array: new Float32Array(2 * 4)
},
index:
{
itemSize: 1,
array: new Uint16Array(6)
}
};
var positions = geometry.attributes.position.array;
var uvs = geometry.attributes.uv.array;
var indices = geometry.attributes.index.array;
var i;
for(i = 0; i < positions.length; i += 3)
{
positions[i] = quad_vertices[i];
positions[i + 1] = quad_vertices[i + 1];
positions[i + 2] = quad_vertices[i + 2];
}
for(i = 0; i < uvs.length; i += 2)
{
uvs[i] = quad_uvs[i];
uvs[i + 1] = quad_uvs[i + 1];
}
for(i = 0; i < indices.length; i++)
indices[i] = quad_indices[i];
var texture = THREE.ImageUtils.loadTexture('./assets/texture.png');
texture.anisotropy = renderer.getMaxAnisotropy();
var material = new THREE.MeshBasicMaterial( { map: texture } );
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -100;
scene.add(mesh);
Just creating mesh with THREE.Geometry is OK so I have no idea what can be wrong with this code. Any thoughts?
Here is a working example of indexed BufferGeometry with uvs. I updated your example to work with three.js r83. I saw two problems with the old code. First, you can't just set geometry.attributes equal to a JSON object definition. THREE.BufferAttribute is a class, but your JSON is missing the function definitions on its prototype that are required by the THREE.Renderer. Second THREE.ImageUtils has been replaced by THREE.TextureLoader, so I updated that in the example as well.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var quad_vertices =
[
-30.0, 30.0, 0.0,
30.0, 30.0, 0.0,
30.0, -30.0, 0.0,
-30.0, -30.0, 0.0
];
var quad_uvs =
[
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
];
var quad_indices =
[
0, 2, 1, 0, 3, 2
];
var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array( quad_vertices );
// Each vertex has one uv coordinate for texture mapping
var uvs = new Float32Array( quad_uvs);
// Use the four vertices to draw the two triangles that make up the square.
var indices = new Uint32Array( quad_indices )
// itemSize = 3 because there are 3 values (components) per vertex
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
// Load the texture asynchronously
var textureLoader = new THREE.TextureLoader();
textureLoader.load('./assets/texture.jpg', function (texture){
console.log('texture loaded');
var material = new THREE.MeshBasicMaterial( {map: texture });
var mesh = new THREE.Mesh( geometry, material );
mesh.position.z = -100;
scene.add(mesh);
renderer.render(scene, camera);
}, undefined, function (err) {
console.error('texture not loaded', err)
});
For further reference:
Creating a scene
BufferAttribute
For those looking to combine an indexed buffer geometry with a texture and a custom shader material (I believe this approaches the upper bound of performance), I used the following approach. All of the real work happens in loadImage() and in the vertex and fragment shaders, the rest is just boilerplate to set up Three.js (version 92):
/**
* Generate a scene object with a background color
**/
function getScene() {
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
return scene;
}
/**
* Generate the camera to be used in the scene. Camera args:
* [0] field of view: identifies the portion of the scene
* visible at any time (in degrees)
* [1] aspect ratio: identifies the aspect ratio of the
* scene in width/height
* [2] near clipping plane: objects closer than the near
* clipping plane are culled from the scene
* [3] far clipping plane: objects farther than the far
* clipping plane are culled from the scene
**/
function getCamera() {
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 1000);
camera.position.set(0, 1, 10);
return camera;
}
/**
* Generate the renderer to be used in the scene
**/
function getRenderer() {
// Create the canvas with a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
// Add support for retina displays
renderer.setPixelRatio(window.devicePixelRatio);
// Specify the size of the canvas
renderer.setSize(window.innerWidth, window.innerHeight);
// Add the canvas to the DOM
document.body.appendChild(renderer.domElement);
return renderer;
}
/**
* Generate the controls to be used in the scene
* #param {obj} camera: the three.js camera for the scene
* #param {obj} renderer: the three.js renderer for the scene
**/
function getControls(camera, renderer) {
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.zoomSpeed = 0.4;
controls.panSpeed = 0.4;
return controls;
}
/**
* Load image
**/
function loadImage() {
var geometry = new THREE.BufferGeometry();
/*
Now we need to push some vertices into that geometry to identify the coordinates the geometry should cover
*/
// Identify the image size
var imageSize = {width: 10, height: 7.5};
// Identify the x, y, z coords where the image should be placed
var coords = {x: -5, y: -3.75, z: 0};
// Add one vertex for each corner of the image, using the
// following order: lower left, lower right, upper right, upper left
var vertices = new Float32Array([
coords.x, coords.y, coords.z, // bottom left
coords.x+imageSize.width, coords.y, coords.z, // bottom right
coords.x+imageSize.width, coords.y+imageSize.height, coords.z, // upper right
coords.x, coords.y+imageSize.height, coords.z, // upper left
])
// set the uvs for this box; these identify the following corners:
// lower-left, lower-right, upper-right, upper-left
var uvs = new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
])
// indices = sequence of index positions in `vertices` to use as vertices
// we make two triangles but only use 4 distinct vertices in the object
// the second argument to THREE.BufferAttribute is the number of elements
// in the first argument per vertex
geometry.setIndex([0,1,2, 2,3,0])
geometry.addAttribute('position', new THREE.BufferAttribute( vertices, 3 ));
geometry.addAttribute('uv', new THREE.BufferAttribute( uvs, 2) )
// Create a texture loader so we can load our image file
var loader = new THREE.TextureLoader();
// specify the url to the texture
var url = 'https://s3.amazonaws.com/duhaime/blog/tsne-webgl/assets/cat.jpg';
// specify custom uniforms and attributes for shaders
// Uniform types: https://github.com/mrdoob/three.js/wiki/Uniforms-types
var material = new THREE.ShaderMaterial({
uniforms: {
texture: {
type: 't',
value: loader.load(url)
},
},
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
// Combine our image geometry and material into a mesh
var mesh = new THREE.Mesh(geometry, material);
// Set the position of the image mesh in the x,y,z dimensions
mesh.position.set(0,0,0)
// Add the image to the scene
scene.add(mesh);
}
/**
* Render!
**/
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
};
var scene = getScene();
var camera = getCamera();
var renderer = getRenderer();
var controls = getControls(camera, renderer);
loadImage();
render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script type='x-shader/x-vertex' id='vertex-shader'>
/**
* The vertex shader's main() function must define `gl_Position`,
* which describes the position of each vertex in the space.
*
* To do so, we can use the following variables defined by Three.js:
*
* uniform mat4 modelViewMatrix - combines:
* model matrix: maps a point's local coordinate space into world space
* view matrix: maps world space into camera space
*
* uniform mat4 projectionMatrix - maps camera space into screen space
*
* attribute vec3 position - sets the position of each vertex
*
* attribute vec2 uv - determines the relationship between vertices and textures
*
* `uniforms` are constant across all vertices
*
* `attributes` can vary from vertex to vertex and are defined as arrays
* with length equal to the number of vertices. Each index in the array
* is an attribute for the corresponding vertex
*
* `varyings` are values passed from the vertex to the fragment shader
**/
varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
/**
* The fragment shader's main() function must define `gl_FragColor`,
* which describes the pixel color of each pixel on the screen.
*
* To do so, we can use uniforms passed into the shader and varyings
* passed from the vertex shader
**/
precision highp float; // set float precision (optional)
uniform sampler2D texture; // identify the texture as a uniform argument
varying vec2 vUv; // identify the uv values as a varying attribute
void main() {
gl_FragColor = texture2D(texture, vUv);
}
</script>

Resources