Skip to content

Commit cd54f8d

Browse files
phong phong phong (#5001)
1 parent 811bd5f commit cd54f8d

File tree

6 files changed

+239
-1
lines changed

6 files changed

+239
-1
lines changed

docs/components/material.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,30 @@ For example, for a tree bark material, as an estimation, we might set:
147147
</a-entity>
148148
```
149149

150+
#### Phong-Based Shading
151+
152+
Phong shading is an inexpensive shader model which whilst less realistic than the
153+
standard material is better than flat shading.
154+
155+
To use it set the shader to phong in the material:
156+
157+
```html
158+
<a-torus-knot position="0 3 0" material="shader:phong; reflectivity: 0.9; shininess: 30;"
159+
geometry="radius: 0.45; radiusTubular: 0.09">
160+
</a-torus-knot>
161+
```
162+
163+
It has the following properties you can use:
164+
165+
| Name | Description | Default |
166+
|----------------|-------------------------------------------------------------------------------|---------|
167+
|specular | This defines how shiny the material is and the color of its shine. | #111111 |
168+
|shininess | How shiny the specular highlight is; a higher value gives a sharper highlight | 30 |
169+
|transparent | Whether the material is transparent | false |
170+
|combine | How the environment map mixes with the material. "mix", "add" or "multiply" | "mix" |
171+
|reflectivity | How much the environment map affects the surface | 0.9 |
172+
|refract | Whether the defined envMap should refract | false |
173+
|refractionRatio | 1/refractive index of the material | 0.98 |
150174
#### Distortion Maps
151175

152176
There are three properties which give the illusion of complex geometry:

examples/boilerplate/webxr-ar-lighting/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
</a-box>
4949
<a-sphere position="0 1.25 -1" radius="1.25" color="#cccccc" material="roughness:0.5; metalness:0.1;">
5050
</a-sphere>
51-
<a-torus-knot position="0 3 0" material="metalness: 1; roughness: 0.17"
51+
<a-torus-knot position="0 3 0" material="shader:phong; reflectivity: 0.9; shininess: 100;"
5252
geometry="radius: 0.45; radiusTubular: 0.09"
5353
animation__rotate="easing: linear; from: 0 0 0; loop: true; property: rotation; to: 0 0 360; dur: 3000;">
5454
</a-torus-knot>

src/components/scene/reflection.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,11 @@ module.exports.Component = register('reflection', {
138138
}
139139

140140
if (this.needsVREnvironmentUpdate) {
141+
scene.environment = null;
141142
this.needsVREnvironmentUpdate = false;
142143
this.cubeCamera.position.set(0, 1.6, 0);
143144
this.cubeCamera.update(renderer, scene);
145+
this.el.object3D.environment = this.cubeRenderTarget.texture;
144146
}
145147

146148
if (this.needsLightProbeUpdate && frame) {

src/shaders/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require('./flat');
22
require('./standard');
3+
require('./phong');
34
require('./sdf');
45
require('./msdf');
56
require('./ios10hls');

src/shaders/phong.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
var registerShader = require('../core/shader').registerShader;
2+
var THREE = require('../lib/three');
3+
var utils = require('../utils/');
4+
5+
var CubeLoader = new THREE.CubeTextureLoader();
6+
var texturePromises = {};
7+
8+
/**
9+
* Phong shader using THREE.MeshPhongMaterial.
10+
*/
11+
module.exports.Shader = registerShader('phong', {
12+
schema: {
13+
color: { type: 'color' },
14+
emissive: { type: 'color', default: 'black' },
15+
emissiveIntensity: { default: 1 },
16+
specular: { type: 'color', default: '#111111' },
17+
transparent: { default: false },
18+
fog: { default: true },
19+
offset: { type: 'vec2', default: { x: 0, y: 0 } },
20+
repeat: { type: 'vec2', default: { x: 1, y: 1 } },
21+
src: { type: 'map' },
22+
envMap: { default: '' },
23+
sphericalEnvMap: { type: 'map' },
24+
shininess: { default: 30 },
25+
flatShading: { default: false },
26+
wireframe: { default: false },
27+
wireframeLinewidth: { default: 2 },
28+
combine: { oneOF: ['multiply', 'mix', 'add'], default: 'mix' },
29+
reflectivity: { default: 0.9 },
30+
refractionRatio: { default: 0.98 },
31+
refract: { default: false },
32+
33+
normalMap: { type: 'map' },
34+
normalScale: { type: 'vec2', default: { x: 1, y: 1 } },
35+
normalTextureOffset: { type: 'vec2' },
36+
normalTextureRepeat: { type: 'vec2', default: { x: 1, y: 1 } },
37+
38+
displacementMap: { type: 'map' },
39+
displacementScale: { default: 1 },
40+
displacementBias: { default: 0.5 },
41+
displacementTextureOffset: { type: 'vec2' },
42+
displacementTextureRepeat: { type: 'vec2', default: { x: 1, y: 1 } },
43+
44+
bumpMap: { type: 'map' },
45+
bumpMapScale: { default: 1 },
46+
bumpTextureOffset: { type: 'vec2' },
47+
bumpTextureRepeat: { type: 'vec2', default: { x: 1, y: 1 } }
48+
},
49+
50+
/**
51+
* Initializes the shader.
52+
* Adds a reference from the scene to this entity as the camera.
53+
*/
54+
init: function (data) {
55+
this.rendererSystem = this.el.sceneEl.systems.renderer;
56+
this.materialData = { color: new THREE.Color(), specular: new THREE.Color(), emissive: new THREE.Color() };
57+
this.textureSrc = null;
58+
getMaterialData(data, this.materialData);
59+
this.rendererSystem.applyColorCorrection(this.materialData.color);
60+
this.material = new THREE.MeshPhongMaterial(this.materialData);
61+
utils.material.updateMap(this, data);
62+
},
63+
64+
update: function (data) {
65+
this.updateMaterial(data);
66+
utils.material.updateMap(this, data);
67+
if (data.normalMap) { utils.material.updateDistortionMap('normal', this, data); }
68+
if (data.displacementMap) { utils.material.updateDistortionMap('displacement', this, data); }
69+
if (data.ambientOcclusionMap) { utils.material.updateDistortionMap('ambientOcclusion', this, data); }
70+
if (data.bump) { utils.material.updateDistortionMap('bump', this, data); }
71+
this.updateEnvMap(data);
72+
},
73+
74+
/**
75+
* Updating existing material.
76+
*
77+
* @param {object} data - Material component data.
78+
*/
79+
updateMaterial: function (data) {
80+
var key;
81+
getMaterialData(data, this.materialData);
82+
this.rendererSystem.applyColorCorrection(this.materialData.color);
83+
for (key in this.materialData) {
84+
this.material[key] = this.materialData[key];
85+
}
86+
},
87+
88+
/**
89+
* Handle environment cubemap. Textures are cached in texturePromises.
90+
*/
91+
updateEnvMap: function (data) {
92+
var self = this;
93+
var material = this.material;
94+
var envMap = data.envMap;
95+
var sphericalEnvMap = data.sphericalEnvMap;
96+
var refract = data.refract;
97+
var sceneEl = this.el.sceneEl;
98+
99+
// No envMap defined or already loading.
100+
if ((!envMap && !sphericalEnvMap) || this.isLoadingEnvMap) {
101+
Object.defineProperty(material, 'envMap', {
102+
get: function () {
103+
return sceneEl.object3D.environment;
104+
},
105+
set: function (value) {
106+
delete this.envMap;
107+
this.envMap = value;
108+
}
109+
});
110+
material.needsUpdate = true;
111+
return;
112+
}
113+
this.isLoadingEnvMap = true;
114+
delete material.envMap;
115+
116+
// if a spherical env map is defined then use it.
117+
if (sphericalEnvMap) {
118+
this.el.sceneEl.systems.material.loadTexture(sphericalEnvMap, { src: sphericalEnvMap }, function textureLoaded (texture) {
119+
self.isLoadingEnvMap = false;
120+
texture.mapping = refract ? THREE.EquirectangularRefractionMapping : THREE.EquirectangularReflectionMapping;
121+
122+
material.envMap = texture;
123+
utils.material.handleTextureEvents(self.el, texture);
124+
material.needsUpdate = true;
125+
});
126+
return;
127+
}
128+
129+
// Another material is already loading this texture. Wait on promise.
130+
if (texturePromises[envMap]) {
131+
texturePromises[envMap].then(function (cube) {
132+
self.isLoadingEnvMap = false;
133+
material.envMap = cube;
134+
utils.material.handleTextureEvents(self.el, cube);
135+
material.needsUpdate = true;
136+
});
137+
return;
138+
}
139+
140+
// Material is first to load this texture. Load and resolve texture.
141+
texturePromises[envMap] = new Promise(function (resolve) {
142+
utils.srcLoader.validateCubemapSrc(envMap, function loadEnvMap (urls) {
143+
CubeLoader.load(urls, function (cube) {
144+
// Texture loaded.
145+
self.isLoadingEnvMap = false;
146+
material.envMap = cube;
147+
cube.mapping = refract ? THREE.CubeRefractionMapping : THREE.CubeReflectionMapping;
148+
utils.material.handleTextureEvents(self.el, cube);
149+
resolve(cube);
150+
});
151+
});
152+
});
153+
}
154+
});
155+
156+
/**
157+
* Builds and normalize material data, normalizing stuff along the way.
158+
*
159+
* @param {object} data - Material data.
160+
* @param {object} materialData - Object to use.
161+
* @returns {object} Updated materialData.
162+
*/
163+
function getMaterialData (data, materialData) {
164+
materialData.color.set(data.color);
165+
materialData.specular.set(data.emissive);
166+
materialData.emissive.set(data.emissive);
167+
materialData.emissiveIntensity = data.emissiveIntensity;
168+
materialData.fog = data.fog;
169+
materialData.transparent = data.transparent;
170+
materialData.wireframe = data.wireframe;
171+
materialData.wireframeLinewidth = data.wireframeLinewidth;
172+
materialData.shininess = data.shininess;
173+
materialData.flatShading = data.flatShading;
174+
materialData.wireframe = data.wireframe;
175+
materialData.wireframeLinewidth = data.wireframeLinewidth;
176+
materialData.reflectivity = data.reflectivity;
177+
materialData.refractionRatio = data.refractionRatio;
178+
179+
switch (data.combine) {
180+
case 'mix':
181+
materialData.combine = THREE.MixOperation;
182+
break;
183+
case 'multiply':
184+
materialData.combine = THREE.MultiplyOperation;
185+
break;
186+
case 'add':
187+
materialData.combine = THREE.AddOperation;
188+
break;
189+
}
190+
191+
if (data.normalMap) {
192+
materialData.normalScale = data.normalScale;
193+
}
194+
195+
if (data.ambientOcclusionMap) {
196+
materialData.aoMapIntensity = data.ambientOcclusionMapIntensity;
197+
}
198+
199+
if (data.bumpMap) {
200+
materialData.aoMapIntensity = data.bumpMapScale;
201+
}
202+
203+
if (data.displacementMap) {
204+
materialData.displacementScale = data.displacementScale;
205+
materialData.displacementBias = data.displacementBias;
206+
}
207+
208+
return materialData;
209+
}

src/shaders/standard.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ module.exports.Shader = registerShader('standard', {
2222
displacementBias: {default: 0.5},
2323
displacementTextureOffset: {type: 'vec2'},
2424
displacementTextureRepeat: {type: 'vec2', default: {x: 1, y: 1}},
25+
2526
emissive: {type: 'color', default: '#000'},
2627
emissiveIntensity: {default: 1},
28+
2729
envMap: {default: ''},
2830

2931
fog: {default: true},

0 commit comments

Comments
 (0)