user1871801
user1871801

Reputation: 43

Three.js material texture and color

I trying to make a material for a car model with three.js where the base color of the car can be changed dynamically. This was easy enough by changing the color attribute of the MeshPhongMaterial that I am using. I then needed to apply a texture over the top and thought this would be easy enough by adding an image to the map attribute of the material. The outcome was not what I expected though, the map/texture image was also shaded the color set by the color attribute. I want it to basically overlay the base color.

Can anyone point me in the right direction?

Upvotes: 4

Views: 7558

Answers (2)

bjorke
bjorke

Reputation: 3305

Here is a complete script chunk. Insert immediately after reading three.js

<script>
THREE.ShaderChunk.map_fragment = [
    "#ifdef USE_MAP",
        "vec4 texelColor = texture2D( map, vUv ); /* NEWWW */",
        "#ifdef GAMMA_INPUT",
        "texelColor.xyz *= texelColor.xyz;",
        "#endif",
        "gl_FragColor.rgb = mix(gl_FragColor.rgb,texelColor.rgb,texelColor.a);",
        "vec3 surfDiffuse = mix(diffuse,vec3(1,1,1),texelColor.a);",
    "#else",
        "vec3 surfDiffuse = diffuse;",
    "#endif"].join('\n');
// now replace references to 'diffuse' with 'surfDiffuse'
THREE.ShaderChunk.lights_phong_fragment = 
    THREE.ShaderChunk.lights_phong_fragment.replace(/\bdiffuse\b/gm,'surfDiffuse')
THREE.ShaderLib.phong.fragmentShader = [
    "uniform vec3 diffuse;",
    "uniform float opacity;",
    "uniform vec3 ambient;",
    "uniform vec3 emissive;",
    "uniform vec3 specular;",
    "uniform float shininess;",
    THREE.ShaderChunk[ "color_pars_fragment" ],
    THREE.ShaderChunk[ "map_pars_fragment" ],
    THREE.ShaderChunk[ "lightmap_pars_fragment" ],
    THREE.ShaderChunk[ "envmap_pars_fragment" ],
    THREE.ShaderChunk[ "fog_pars_fragment" ],
    THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
    THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
    THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
    THREE.ShaderChunk[ "normalmap_pars_fragment" ],
    THREE.ShaderChunk[ "specularmap_pars_fragment" ],
    "void main() {",
        "gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
        THREE.ShaderChunk[ "map_fragment" ],
        THREE.ShaderChunk[ "alphatest_fragment" ],
        THREE.ShaderChunk[ "specularmap_fragment" ],
        THREE.ShaderChunk[ "lights_phong_fragment" ],
        THREE.ShaderChunk[ "lightmap_fragment" ],
        THREE.ShaderChunk[ "color_fragment" ],
        THREE.ShaderChunk[ "envmap_fragment" ],
        THREE.ShaderChunk[ "shadowmap_fragment" ],
        THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
        THREE.ShaderChunk[ "fog_fragment" ],
    "}"
].join('\n');
</script>

learned a little bit about the three.js phong material -- it doesn't handle highlights the way I'd like, but... whatever. If you need more sophistication I'd suggest using ShaderMaterial instead.

Upvotes: 3

bjorke
bjorke

Reputation: 3305

You will need a custom shader for that -- and a texture with an alpha channel (essentially, a DDS format). If you look in the three.js source, you'll see a bit of fragment-shader code called 'map_fragment' that looks like so:

    "#ifdef USE_MAP",

        "#ifdef GAMMA_INPUT",

            "vec4 texelColor = texture2D( map, vUv );",
            "texelColor.xyz *= texelColor.xyz;",

            "gl_FragColor = gl_FragColor * texelColor;",

        "#else",

            "gl_FragColor = gl_FragColor * texture2D( map, vUv );",

        "#endif",

    "#endif"

As you can see it multiplies your texture map by the color (which at that point is in "gl_FragColor"). Instead, it sound like you want your map to overlay on top of the base color, only were you've painted it (say, chrome bumpers and headlights).

A simple way to do this might be to just alter the way 'phong' works before you instantiate any phong materials, by changing the value of that "map_fragment" string.

Instead of "gl_FragColor * (something)" -- try this as a re-assignment of THREE.ShaderChunk.map_fragment:

 [

    "#ifdef USE_MAP",

        "vec4 texelColor = texture2D( map, vUv );",

        "#ifdef GAMMA_INPUT",

            "texelColor.xyz *= texelColor.xyz;",

        "#endif",

        "gl_FragColor = vec4(mix(gl_FragColor.rgb,texelColor.rgb,texelColor.a),(gl_FragColor.a*texelColor.a));",

    "#endif"

].join('\n');

The hazard with overriding the stock method this way is that it will apply to ALL models in the scene -- the painted texture will overlay the material color. If that's okay, you're done. Otherwise you should create a new THREE.ShaderMaterial that's like "phong" except for the difference cited here.

Upvotes: 0

Related Questions