Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support per-map per-Material UV channels, UV offset/repeat and texel scale/offset #8278

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9c21432
add TextureSlot support to MeshStandardMaterial with perfect backward…
bhouston Mar 2, 2016
03eeb55
convert WebGLPrograms to auto-generate parameters based on slots with…
bhouston Mar 2, 2016
d21f843
create glsl injectors for slot UV and Texel transforms.
bhouston Mar 2, 2016
c36a7b9
do code glsl injection when there are texture slots used.
bhouston Mar 2, 2016
6f22082
integrated in TEXTURE_SLOTS into fragment shaders.
bhouston Mar 2, 2016
5d4a186
fix cut and paste mistake.
bhouston Mar 2, 2016
4a41dc6
allow slows to work on all materials. set parameters.
bhouston Mar 2, 2016
a230324
comment out feature not yet implemented.
bhouston Mar 2, 2016
92850e9
only defined the supported slots once.
bhouston Mar 2, 2016
6d67678
other PR leaking into this one.
bhouston Mar 2, 2016
d829a1d
forgotten 'displacementMap' texture slot name.
bhouston Mar 2, 2016
9b17d3f
remove duplicate map entry.
bhouston Mar 3, 2016
db20880
better organization of mesh standard material.
bhouston Mar 3, 2016
c1baf22
fix ordering of uvrepeat/uvoffset when loading TextureSlot uniforms.
bhouston Mar 3, 2016
708268a
add texture slot example.
bhouston Mar 3, 2016
e7afdef
polishing example.
bhouston Mar 3, 2016
7c782a1
more example tweaking to get something decent.
bhouston Mar 3, 2016
78d4e69
better title.
bhouston Mar 3, 2016
0375a7b
example teweaks.
bhouston Mar 3, 2016
34e9380
rename TextureSlot -> Map - better name.
bhouston Mar 3, 2016
94290c5
add slots to example file list.
bhouston Mar 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ var files = {
"webgl_materials_parallaxmap",
"webgl_materials_shaders_fresnel",
"webgl_materials_skin",
"webgl_materials_slots",
"webgl_materials_standard",
"webgl_materials_texture_anisotropy",
"webgl_materials_texture_compressed",
Expand Down
309 changes: 309 additions & 0 deletions examples/webgl_materials_slots.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>threejs webgl - per-mat per-material uv offset/repeat and texel scale/offset/invert</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 {
color: #000;
font-family:Monospace;
font-size:13px;
text-align:center;

background-color: #000;

margin: 0px;
overflow: hidden;
}

a { color: #222 }

#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
</style>
</head>
<body>

<div id="container"></div>
<div id="info"><a href="http://threejs.org" target="_blank">threejs</a> - Per-Map Per-Material UV Channel, UV Repeat/Offset, and Texel Scale/Offset/Invert<br/>via THREE.Map by <a href="http://clara.io/" target="_blank">Ben Houston</a>.</div>

<script src="../build/three.min.js"></script>
<script src="../examples/js/controls/OrbitControls.js"></script>
<script src="../src/loaders/BinaryTextureLoader.js"></script>

<script src="../examples/js/Detector.js"></script>
<script src="../examples/js/libs/stats.min.js"></script>

<script src="../examples/js/libs/dat.gui.min.js"></script>
<script src="../src/loaders/BinaryTextureLoader.js"></script>
<script src="../examples/js/loaders/RGBELoader.js"></script>
<script src="../examples/js/loaders/HDRCubeTextureLoader.js"></script>
<script src="../examples/js/Half.js"></script>
<script src="../examples/js/Encodings.js"></script>
<script src="../examples/js/pmrem/PMREMGenerator.js"></script>
<script src="../examples/js/pmrem/PMREMCubeUVPacker.js"></script>

<script src="../examples/js/postprocessing/EffectComposer.js"></script>
<script src="../examples/js/postprocessing/RenderPass.js"></script>
<script src="../examples/js/postprocessing/MaskPass.js"></script>
<script src="../examples/js/postprocessing/ShaderPass.js"></script>
<script src="../examples/js/shaders/CopyShader.js"></script>
<script src="../examples/js/shaders/FXAAShader.js"></script>
<script src="../examples/js/postprocessing/BloomPass.js"></script>
<script src="../examples/js/shaders/ConvolutionShader.js"></script>

<script>

if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

var container, stats;
var params;
var camera, scene, renderer, controls, objects = [];
var composer;
var standardMaterial, standardMaterialPremultiplied, floorMaterial;

init();
animate();

function init() {

container = document.createElement( 'div' );
document.body.appendChild( container );

camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 0.0, 80, 40 * 3.5 );

scene = new THREE.Scene();

renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( new THREE.Color( 0xffffff ) );

standardMaterial = new THREE.MeshStandardMaterial( {
bumpScale: - 0.05,
color: 0xffffff,
emissive: 0xffffff,
metalness: 0.9,
roughness: 0.8,
normalScale: new THREE.Vector2( 1, -1 ),
shading: THREE.SmoothShading,
transparent: true,
blending: THREE.PremultipliedAlphaBlending
} );

var textureLoader = new THREE.TextureLoader();
textureLoader.load( "../examples/textures/planets/earth_atmos_2048.jpg", function( map ) {
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 4;
standardMaterial.map = map;
standardMaterial.needsUpdate = true;
} );
textureLoader.load( "../examples/textures/planets/earth_normal_2048.jpg", function( map ) {
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 4;
standardMaterial.normalMap = map;
standardMaterial.needsUpdate = true;
} );
textureLoader.load( "../examples/textures/planets/earth_specular_2048.jpg", function( map ) {
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 4;
standardMaterial.roughnessMap = map;
standardMaterial.needsUpdate = true;
} );
textureLoader.load( "../examples/textures/planets/earth_lights_2048.png", function( map ) {
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 4;
standardMaterial.emissiveMap = map;
standardMaterial.needsUpdate = true;
} );


var geometry = new THREE.SphereGeometry( 35, 48, 48 );
var torusMesh1 = new THREE.Mesh( geometry, standardMaterial );
torusMesh1.position.x = 0.0;
torusMesh1.castShadow = true;
torusMesh1.receiveShadow = true;
scene.add( torusMesh1 );
objects.push( torusMesh1 );

// Materials
var hdrpath = "../examples/textures/cube/hdrPisa/";
var hdrformat = '.hdr';
var hdrurls = [
hdrpath + 'px' + hdrformat, hdrpath + 'nx' + hdrformat,
hdrpath + 'py' + hdrformat, hdrpath + 'ny' + hdrformat,
hdrpath + 'pz' + hdrformat, hdrpath + 'nz' + hdrformat
];

var hdrCubeMap = new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, hdrurls, function ( hdrCubeMap ) {

var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
pmremGenerator.update( renderer );

var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
pmremCubeUVPacker.update( renderer );

standardMaterial.envMap = pmremCubeUVPacker.CubeUVRenderTarget;
standardMaterial.envMapIntensity = 3;
standardMaterial.needsUpdate = true;

} );

// Lights

//scene.add( new THREE.AmbientLight( 0x222222 ) );

var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 50, 100, 50 );
spotLight.angle = Math.PI / 7;
spotLight.penumbra = 0.8
spotLight.castShadow = true;
scene.add( spotLight );

renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
container.appendChild( renderer.domElement );

renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 6;
renderer.gammaInput = true;
renderer.gammaOutput = true;

composer = new THREE.EffectComposer( renderer );
composer.setSize( window.innerWidth, window.innerHeight );

var renderScene = new THREE.RenderPass( scene, camera );
composer.addPass( renderScene );

var copyPass = new THREE.ShaderPass( THREE.CopyShader );
copyPass.renderToScreen = true;
composer.addPass( copyPass );

stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';

container.appendChild( stats.domElement );

controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0, 0 );
controls.update();

window.addEventListener( 'resize', onWindowResize, false );

var gui = new dat.GUI();

params = {};

var mapParams = function( mapName ) {
params[ mapName + 'TexelInvert'] = ( mapName === 'rough' );
gui.add( params, mapName + 'TexelInvert' );
if( mapName === "bump" ) {
params[ mapName + 'TexelOffset'] = 0;
params[ mapName + 'TexelScale'] = 0.05;
gui.add( params, mapName + 'TexelScale', -0.1, 0.1 );
}
else {
params[ mapName + 'TexelOffset'] = ( mapName === 'rough' ) ? 0.4 : 0;
if( mapName === 'rough' ) params[ mapName + 'TexelScale'] = 0.6;
else if( mapName === 'emissive' ) params[ mapName + 'TexelScale'] = 0.3;
else params[ mapName + 'TexelScale'] = 1.0;
if( mapName !== "normal" ) {
gui.add( params, mapName + 'TexelOffset', -1, 1 );
gui.add( params, mapName + 'TexelScale', -1, 1 );
}
}
params[ mapName + 'UVOffsetX'] = 0;
gui.add( params, mapName + 'UVOffsetX', 0, 1 );
params[ mapName + 'UVOffsetY'] = 0;
gui.add( params, mapName + 'UVOffsetY', 0, 1 );
params[ mapName + 'UVRepeatX'] = 1;
gui.add( params, mapName + 'UVRepeatX', 0.01, 5 );
params[ mapName + 'UVRepeatY'] = 1;
gui.add( params, mapName + 'UVRepeatY', 0.01, 5 );
};
mapParams( "map" );
mapParams( "normal" );
mapParams( "rough" );
mapParams( "emissive" );
gui.open();

}

function onWindowResize() {

var width = window.innerWidth;
var height = window.innerHeight;

camera.aspect = width / height;
camera.updateProjectionMatrix();

renderer.setSize( width, height );
composer.setSize( width, height );

}

//

function animate() {

requestAnimationFrame( animate );

render();
stats.update();

}

function render() {

if ( standardMaterial !== undefined ) {

var setSlot = function( map, name ) {
map.texelTransform = true;
map.texelScale = params[ name + 'TexelScale'];
map.texelOffset = params[ name + 'TexelOffset'];
map.texelInvert = params[ name + 'TexelInvert'];
map.uvTransform = true;
map.uvRepeat.x = params[ name + 'UVRepeatX'];
map.uvRepeat.y = params[ name + 'UVRepeatY'];
map.uvOffset.x = params[ name + 'UVOffsetX'];
map.uvOffset.y = params[ name + 'UVOffsetY'];
}
setSlot( standardMaterial.mapSlot, "map" );
setSlot( standardMaterial.normalMapSlot, "normal" );
setSlot( standardMaterial.roughnessMapSlot, "rough" );
setSlot( standardMaterial.emissiveMapSlot, "emissive" );
}

var timer = Date.now() * 0.00025;

camera.lookAt( scene.position );

for ( var i = 0, l = objects.length; i < l; i ++ ) {

var object = objects[ i ];
object.rotation.y += 0.005;

}

if( params.renderMode === "Composer" ) {
composer.render();
}
else {
renderer.render( scene, camera );
}

}

</script>

</body>
</html>
71 changes: 71 additions & 0 deletions src/materials/Map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @author Ben Houston / bhouston / http://clara.io
*
*/

THREE.Map = function ( name, uvChannel, uvTransform, texelTransform ) {

this.name = name || "unnamed";

this.texture = null;

this.uvChannel = uvChannel || 0;

this.uvTransform = uvTransform || false;
this.uvOffset = new THREE.Vector2( 0, 0 );
this.uvRepeat = new THREE.Vector2( 1.0, 1.0 );
//this.uvRotation = 0; - not implemented because offset/repeat fix in a vec4 uniform, rotation doesn't.

this.texelTransform = uvTransform || false;
this.texelScale = 1.0;
this.texelOffset = 0.0;
this.texelInvert = false;

};

THREE.Map.prototype = {

constructor: THREE.Map,

copy: function ( source ) {

this.name = source.name;

this.texture = source.texture;

this.uvChannel = source.uvChannel;

this.uvTransform = source.uvTransform;
this.uvOffset = source.uvOffset;
this.uvRepeat = source.uvRepeat;
//this.uvRotation = source.uvRotation;

this.texelTransform = source.texelTransform;
this.texelScale = source.texelScale;
this.texelOffset = source.texelOffset;
this.texelInvert = source.texelInvert;

return this;

},

// bakes all the input texel parameters into just two.
getFlattenedTexelTransform: function() {

if( this.texelInvert ) {
return {
texelScale: -this.texelScale,
texelOffset: this.texelScale + this.texelOffset
};
}
return {
texelScale: this.texelScale,
texelOffset: this.texelOffset
};
}

};

THREE.Map.SupportedMapNames = [
'map', 'lightMap', 'aoMap', 'emissiveMap', 'specularMap', 'bumpMap', 'normalMap', 'roughnessMap', 'metalnessMap', 'alphaMap', 'displacementMap'
];
Loading