After some digging, we found out that our image is just transparent. Once we manually set the opacity, we also saw that it was tinted with a grey color. So we looked through the engine and found this function in system.js
:
_createBaseImageMaterial() {
var material = new StandardMaterial();
material.diffuse.set(0, 0, 0); // black diffuse color to prevent ambient light being included
material.emissive.set(0.5, 0.5, 0.5); // use non-white to compile shader correctly
material.emissiveMap = this._defaultTexture;
material.emissiveTint = true;
material.opacityMap = this._defaultTexture;
material.opacityMapChannel = "a";
material.opacityTint = true;
material.opacity = 0; // use non-1 opacity to compile shader correctly
material.useLighting = false;
material.useGammaTonemap = false;
material.useFog = false;
material.useSkybox = false;
material.blendType = BLEND_PREMULTIPLIED;
material.depthWrite = false;
return material;
}
If I understand correctly, these values of the material will never change. The attributes on the element component will only ever be set on the element instance itself and through it’s setters as a uniform in the shader. So we need to set all the uniforms manually that we need to display the sprite, or force the setters to set the uniform again.
What we also found was this setter in image-element.js
:
set material(value) {
if (this._material === value) return;
if (!value) {
var screenSpace = this._element._isScreenSpace();
if (this.mask) {
value = screenSpace ? this._system.defaultScreenSpaceImageMaskMaterial : this._system.defaultImageMaskMaterial;
} else {
value = screenSpace ? this._system.defaultScreenSpaceImageMaterial : this._system.defaultImageMaterial;
}
}
this._material = value;
if (value) {
this._renderable.setMaterial(value);
// if this is not the default material then clear color and opacity overrides
if (this._hasUserMaterial()) {
this._renderable.deleteParameter('material_opacity');
this._renderable.deleteParameter('material_emissive');
} else {
// otherwise if we are back to the defaults reset the color and opacity
this._colorUniform[0] = this._color.r;
this._colorUniform[1] = this._color.g;
this._colorUniform[2] = this._color.b;
this._renderable.setParameter('material_emissive', this._colorUniform);
this._renderable.setParameter('material_opacity', this._color.a);
}
}
}
Since we are setting a new material, which is now not a base material anymore, this._hasUserMaterial()
returns true and the 2 uniforms 'material_opacity'
and 'material_emissive'
are removed. This is the reason we don’t see anything.
So our current solution would need to be something like this:
TestScript.prototype.initialize() {
var material = this.entity.element.material.clone();
this.entity.element.material = material;
var opacity = this.entity.element.opacity;
this.entity.element.opacity = 0.9; // if we set it to it's own value, the setter is gonna return early
this.entity.element.opacity = opacity;
var color = this.entity.element.color.clone();
this.entity.element.color = new pc.Color(); // if we set it to it's own value, the setter is gonna return early
this.entity.element.color = color;
material.update();
}
This doesn’t seem like the correct way to do it though 