ShaderMaterial does not support textures when replicating MeshLambertMaterial

It came to my attention that THREE.js utilizes shaders internally for creating core material like "e.g. MeshLambertMaterial". Intrigued by this, I decided to replicate the lambert shader from Three.js code into a new shader and expand upon it.

Below is the code I obtained (faithfully copied from Three.js r66)

THREE.MyShader = {

uniforms: THREE.UniformsUtils.merge( [
    THREE.UniformsLib[ "common" ],
    THREE.UniformsLib[ "fog" ],
    THREE.UniformsLib[ "lights" ],
    THREE.UniformsLib[ "shadowmap" ],
    {
        "ambient"  : { type: "c", value: new THREE.Color( 0xffffff ) },
        "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
        "wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
    }
]),

vertexShader: [

    "#define LAMBERT",

    "varying vec3 vLightFront;",

    "#ifdef DOUBLE_SIDED",

        "varying vec3 vLightBack;",

    "#endif",

    THREE.ShaderChunk[ "map_pars_vertex" ],
    THREE.ShaderChunk[ "lightmap_pars_vertex" ],
    THREE.ShaderChunk[ "envmap_pars_vertex" ],
    THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
    THREE.ShaderChunk[ "color_pars_vertex" ],
    THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
    THREE.ShaderChunk[ "skinning_pars_vertex" ],
    THREE.ShaderChunk[ "shadowmap_pars_vertex" ],

    "void main() {",

        THREE.ShaderChunk[ "map_vertex" ],
        THREE.ShaderChunk[ "lightmap_vertex" ],
        THREE.ShaderChunk[ "color_vertex" ],

        THREE.ShaderChunk[ "morphnormal_vertex" ],
        THREE.ShaderChunk[ "skinbase_vertex" ],
        THREE.ShaderChunk[ "skinnormal_vertex" ],
        THREE.ShaderChunk[ "defaultnormal_vertex" ],

        THREE.ShaderChunk[ "morphtarget_vertex" ],
        THREE.ShaderChunk[ "skinning_vertex" ],
        THREE.ShaderChunk[ "default_vertex" ],

        THREE.ShaderChunk[ "worldpos_vertex" ],
        THREE.ShaderChunk[ "envmap_vertex" ],
        THREE.ShaderChunk[ "lights_lambert_vertex" ],
        THREE.ShaderChunk[ "shadowmap_vertex" ],

    "}"

].join("\n"),

fragmentShader: [

    "uniform float opacity;",

    "varying vec3 vLightFront;",

    "#ifdef DOUBLE_SIDED",

        "varying vec3 vLightBack;",

    "#endif",

    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[ "shadowmap_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" ],

        "#ifdef DOUBLE_SIDED",

            //"float isFront = float( gl_FrontFacing );",
            //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;",

            "if ( gl_FrontFacing )",
                "gl_FragColor.xyz *= vLightFront;",
            "else",
                "gl_FragColor.xyz *= vLightBack;",

        "#else",

            "gl_FragColor.xyz *= vLightFront;",

        "#endif",

        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")

}

Here is the code I utilize to set up my uniforms and generate the material.

var textureUsed = 'rock_1';
var texture = THREE.ImageUtils.loadTexture( texturePath + textureUsed + "/diffuse.png");
texture.wrapS   = THREE.RepeatWrapping;
texture.wrapT   = THREE.RepeatWrapping;
texture.repeat.x = 128;
texture.repeat.y = 128;
var shaderUniforms = THREE.UniformsUtils.clone( THREE.MyShader.uniforms );
shaderUniforms[ "map" ].value = texture;
var material =  new THREE.ShaderMaterial({
                    name: "TerrainShader",
                    uniforms    : shaderUniforms,
                    vertexShader: THREE.MyShader.vertexShader,
                    fragmentShader: THREE.MyShader.fragmentShader,
                    fog:false,
                    lights:true
                });

Upon using these parameters to create a MeshLambertMaterial, the lighting and texture repetitions function correctly. However, when applied to ShaderMaterial, the lighting and shadows appear to work but the texture map fails to load. To resolve this, I delved into the code and managed to load the map by implementing a somewhat inelegant "hack" in my code, immediately after defining the material

material.map = true;

Now the texture loads and displays, but it appears that the texture coordinates are incorrect. Instead of repeating as specified, the Shader seems to be disregarding the supplied repeat values.

Why did I require that hack to process my texture and how can I achieve the correct texture repetitions?

Answer №1

While three.js was originally created for simplicity in usage rather than ease of customization, there are plans for potential changes in the future.

To customize the material.defines, follow these steps:

var customDefines = {};

customDefines[ "USE_MAP" ] = "";

Make sure to include these customDefines in the material constructor.

var customMaterial = new THREE.ShaderMaterial({
   name: "CustomShader",
   defines     : customDefines,
   uniforms    : customUniforms,
   vertexShader: THREE.CustomShader.vertexShader,
   fragmentShader: THREE.CustomShader.fragmentShader,
   fog: false,
   lights: true
});

For adjusting the texture repetitions, include the repeat values in your uniforms:

customUniforms[ "offsetRepeat" ].value.set( 0, 0, 2, 2 );

This implementation is specific to three.js version r.66.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

invoking a function through prototype

<script> var Nancy = function(){ this.name = 'nancy' } Nancy.prototype.getNancy = function(){ alert(this.name); } Nancy.prototype.getNancy(); function Bob(){ ...

Error: Failed to clone an object in Electron due to an unhandled promise

I'm in need of assistance with sending files from the main directory to the renderer. Before proceeding, I attempted to check if it was functioning correctly by using console.log(files). However, this resulted in an error message. Can someone please p ...

PHP captures the checkbox value through the $_POST method, whereas jQuery removes the checked class from it

I currently have a form with 2 checkboxes: Registered and Not Registered. If the Registered checkbox is ticked, then 2 additional options should appear: Active and Not Active. If the Registered checkbox is unticked, both the Active and Not Active options ...

Unexpected issue encountered when working with JSON in Node.js

I've searched through countless solutions on stackoverflow, but none of them seem to work for me. It's really frustrating not understanding what's going wrong. Below is the code I'm having trouble with: var data = ""; req.on('dat ...

Ways to extract a specific value from a geojson object using the key name

After making a call to the back-end, I retrieved the below geojson data in the 'data' object. However, when trying to access the values of the 'type' and 'features' keys within the geojson, I encountered difficulties. data["g ...

Debugging Node.js Routes in Visual Studio Code: A Step-by-Step Guide

My application is structured as follows: server.js router routes emails.js index.js In my index.js file, I define the route like this: module.exports = function (app) { app.use('/emails', require('./routes/emails& ...

Implementing a custom button specifically for the month view in JavaScript FullCalendar

I have successfully added a custom button to my JavaScript full calendar code, but I would like this button to be displayed only in the month view. $(document).ready(function() { var calendar = $('#calendar').fullCalendar({ editable: tru ...

Error in Three.js: THREE.Scene is not defined as a constructor

My code won't run due to this error message: TypeError: THREE.Scene is not a constructor I've sourced the three.js file from the official GitHub repository, and these are my files: index.html <html lang="en"> <head> <meta c ...

What is the proper way to implement v-model for a custom component within the Vue render function?

Below is the code that I am working with: ... var label_key_map = { a: "1", b: "2", c: "3", d: "4" } render: (h) => { var form_data = {} for (let key in label_key_map) { var form_item = h( 'FormItem', {props: {prop: key}}, ...

Shopping cart has encountered an issue with storing the data correctly

While I've managed to successfully integrate another service, the challenge now lies in implementing the logic for correctly generating cart items. My goal is to increment the quantity of items in the cart by one with each function call, but it seems ...

JsDoc presents the complete code instead of the commented block

I have a VUE.JS application that needs to be documented. For .VUE components, we are using Vuese. However, when it comes to regular JS files like modules, we decided to use JsDoc. I have installed JsDoc and everything seems fine, but when I generate the HT ...

What is the process for transferring npm package files to my local project?

Despite my efforts to find the answer, I couldn't locate it and so here I am asking again. What I'm looking for: I need to move a file from node_modules to my project in order to work on it. First attempt: I moved the file I wanted to edit An ...

Creating a unique array of non-repeating numbers in ES6:

Looking to create an array of unique random numbers in ES6 without any repeats. Currently, my function is generating an array of random numbers that are repeating: winArray = [...Array(6)].map(() => Math.floor(Math.random() * 53)); Here is a non-ES6 ...

The efficiency of a for loop in Javascript can be influenced by the way the loop

I experimented with three different for loop cases in JavaScript, each performing a seemingly useless action 1000000000 times. Surprisingly, the speed of these codes varied from one another. Initially, I suspected that the difference in performance could ...

Place a list with scrolling and overflow features side by side

Having trouble getting my headers to scroll with overflow auto and white-space nowrap. Can't figure out why it's not working. I want to create hyperlinks within headers to link to specific parts of the website. I have the code for hyperlinking s ...

What is the method for a Greasemonkey script to divide a link into three interconnected links?

My goal is to use Greasemonkey to link Redmine issue numbers found in cgit commit messages to their respective issues or projects. The cgit commit message HTML source looks like this: <a href='/editingmodule/commit/?id=49e4a33e0f8b306ded5'&g ...

Using jQuery to dynamically add a class to elements that have tab focus

I want to apply a css class to elements that are focused by the tab key, without adding the class when focused by a mouse click. I have managed to achieve this using jQuery with the focusin and focusout events. Here is the code that I currently have: $( ...

Updating JQuery function after window is resized

click here Currently, I am using slick-slider to showcase content in 3 columns by utilizing flexbox. The aim is to have the slider activated only on mobile view. This means that when the screen size shrinks down to mobile view, the 3 column flexbox transf ...

Word with the Most Points

I have created a code to determine the highest scoring word as a string, however when I calculate all words and attempt to display the results, I am encountering an issue where all results are showing as: NaN function high(x) { var words = x.split(&ap ...

What initiates Electron after npm processes the package.json file?

As I delve into the realms of JavaScript, HTML, and Electron, a particular question has been playing on my mind - what exactly happens when you run electron . in the "scripts" -> "start" section of package.json? The mysterious way it operates sends shiv ...