How can I color the plane with different colors as squares in the same face?

Hi All,

I want to color the plane with different colors like this image:

In the same face, can I do that?

Use a texture with desired squares?

2 Likes

With a texture?

2 Likes

@prisoner849 Yes with texture

What difficulty to apply a texture to a plane? :thinking:

I want to apply 16 textures in the same face in this case, how can I do that?

The only thing I can think of now is a texture atlas.

1 Like

If you want to use 16 textures instead of one texture, then you have to load all 16 textures into the shader. You can use a textureArray in which you save all textures and then pass this on to your shader. This textureArray would then be the textureAtlas that prisoner mentioned

Here is my textureAtlas class:
In a file i called textures.js

//you need to change this import to your THREE source
import {THREE} from './importManager.js';


export const textures = (function() {

  function _GetImageData( image ) {
    var canvas = document.createElement('canvas');
    canvas.width = image.width;
    canvas.height = image.height;

    var context = canvas.getContext('2d');
    context.drawImage( image, 0, 0 );

    return context.getImageData( 0, 0, image.width, image.height );
  }

  return {
    TextureAtlas: class {
      constructor() {
        this.Create_();
        this.onLoad = () => {};
      }

      Load(atlas, names) {
        this.LoadAtlas_(atlas, names);
      }

      Create_() {
        this.manager_ = new THREE.LoadingManager();
        this.loader_ = new THREE.TextureLoader(this.manager_);
        this.textures_ = {};

        this.manager_.onLoad = () => {
          this.OnLoad_();
        };
      }

      get Info() {
        return this.textures_;
      }

      OnLoad_() {
        for (let k in this.textures_) {
          const atlas = this.textures_[k];
          const data = new Uint8Array(atlas.textures.length * 4 * 1024 * 1024);

          for (let t = 0; t < atlas.textures.length; t++) {
            const curTexture = atlas.textures[t];
            const curData = _GetImageData(curTexture.image);
            const offset = t * (4 * 1024 * 1024);

            data.set(curData.data, offset);
          }
    
          const diffuse = new THREE.DataArrayTexture(data, 1024, 1024, atlas.textures.length);
          diffuse.format = THREE.RGBAFormat;
          diffuse.type = THREE.UnsignedByteType;
          diffuse.minFilter = THREE.LinearMipMapLinearFilter;
          diffuse.magFilter = THREE.LinearFilter;
          diffuse.wrapS = THREE.RepeatWrapping;
          diffuse.wrapT = THREE.RepeatWrapping;
          diffuse.generateMipmaps = true;
          diffuse.encoding = THREE.sRGBEncoding;
				diffuse.needsUpdate = true;
          atlas.atlas = diffuse;
        }

        this.onLoad();
      }

      LoadAtlas_(atlas, names) {
        this.textures_[atlas] = {
          textures: names.map(n => this.loader_.load(n))
        };
      }
    }
  };
})();

In the js file in which I then want to load the textures, I import that:

import {textures} from './textures.js';

your custom shader:

	var uniform = {
		diffuseMap: {},
	}

    //for textureArrays you need to use webGL2 shaders
	this.material = new THREE.RawShaderMaterial({
		glslVersion: THREE.GLSL3,
		uniforms: uniform,
		vertexShader: yourVertexShader,
		fragmentShader: yourFragmentShader,
	});

Calls and fill with textures:

      const diffuse = new textures.TextureAtlas();
      diffuse.Load('diffuse', [
      	'./resources/textures/terrain/sandy-rocks1-albedo-1024.png',
      	'./resources/textures/terrain/worn-bumpy-rock-albedo-1024.png',  	
        './resources/textures/terrain/dirt_01_diffuse-1024.png',
        './resources/textures/terrain/grass1-albedo3-1024.png',
        './resources/textures/terrain/sandyground-albedo-1024.png',
        './resources/textures/terrain/rock-snow-ice-albedo-1024.png',
        './resources/textures/terrain/snow-packed-albedo-1024.png',
        './resources/textures/terrain/Mountainbrownrockseamlesstexture10242.png',
      ]);     
      diffuse.onLoad = () => {     
        this.material.uniforms.diffuseMap.value = diffuse.Info['diffuse'].atlas;
      };  

In your fragmentShader then you need this:

uniform sampler2DArray diffuseMap;
vec3 color = texture(diffuseMap, vec3(uv, textureNumber)).rgb;

textureNumber is the index of the texture you want to use.

These are excerpts from my code. In textures.js you will need to adjust the resolution to match that of your textures. In addition, all textures must have the same resolution.

In the fragment shader you will need to set the boundaries of your uv coordinates for each texture.

2 Likes

I get this error :
Cannot read properties of undefined (reading ‘load’)

And the code is :

  let textures = (function() {

    function _GetImageData( image ) {
      var canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
  
      var context = canvas.getContext('2d');
      context.drawImage( image, 0, 0 );
  
      return context.getImageData( 0, 0, image.width, image.height );
    }
  
    return {
      TextureAtlas: class {
        letructor() {
          this.Create_();
          this.onLoad = () => {};
        }
  
        Load(atlas, names) {
          this.LoadAtlas_(atlas, names);
        }
  
        Create_() {
          this.manager_ = new THREE.LoadingManager();
          this.loader_ = new THREE.TextureLoader(this.manager_);
          this.textures_ = {};
  
          this.manager_.onLoad = () => {
            this.OnLoad_();
          };
        }
  
        get Info() {
          return this.textures_;
        }
  
        OnLoad_() {
          for (let k in this.textures_) {
            let atlas = this.textures_[k];
            let data = new Uint8Array(atlas.textures.length * 4 * 1024 * 1024);
  
            for (let t = 0; t < atlas.textures.length; t++) {
              let curTexture = atlas.textures[t];
              let curData = _GetImageData(curTexture.image);
              let offset = t * (4 * 1024 * 1024);
  
              data.set(curData.data, offset);
            }
      
            let diffuse = new THREE.DataArrayTexture(data, 1024, 1024, atlas.textures.length);
            diffuse.format = THREE.RGBAFormat;
            diffuse.type = THREE.UnsignedByteType;
            diffuse.minFilter = THREE.LinearMipMapLinearFilter;
            diffuse.magFilter = THREE.LinearFilter;
            diffuse.wrapS = THREE.RepeatWrapping;
            diffuse.wrapT = THREE.RepeatWrapping;
            diffuse.generateMipmaps = true;
            diffuse.encoding = THREE.sRGBEncoding;
                  diffuse.needsUpdate = true;
            atlas.atlas = diffuse;
          }
  
          this.onLoad();
        }
  
        LoadAtlas_(atlas, names) {
          this.textures_[atlas] = {
            textures: names.map(n => this.loader_.load(n)) // Here the error.
          };
        }
      }
    };
  })();


  let textureLoader = new THREE.TextureLoader();


let diffuse = new textures.TextureAtlas();
diffuse.Load('diffuse', [
    "/textures/sy.jpg",
    "/textures/2.jpg",
    ..
    ..
    ..
]);     
diffuse.onLoad = () => {     
  material.uniforms.diffuseMap.value = diffuse.Info['diffuse'].atlas;
};
  
/**
 * Test mesh
 */
// Geometry

let geometry = new THREE.PlaneGeometry(1, 1, 32, 32);


// Material
let material = new THREE.RawShaderMaterial({
  glslVersion: THREE.GLSL3,
  vertexShader: `
        uniform mat4 projectionMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 modelMatrix;
        uniform vec2 uFrequency;
        uniform float uTime;
       
        in vec3 position;
        out vec2 uv;
        out vec2 vUv;
        
        void main()
        {
            vec4 modelPosition = modelMatrix * vec4(position,1.0);
            modelPosition.z += sin(modelPosition.x * uFrequency.x - uTime) * 0.1;
            modelPosition.z += sin(modelPosition.y * uFrequency.y - uTime) * 0.1;

            vec4 viewPosition = viewMatrix * modelPosition;
            vec4 projectionPosition = projectionMatrix * viewPosition ;
        
             gl_Position = projectionPosition; 
             
             vUv = uv;
                  }
    `,

  fragmentShader: `
        precision highp float;
        precision highp sampler2DArray;

        uniform vec3 uColor;  
        uniform int textureNumber;  

         in  vec2 vUv;
         out vec4 colorFinish;

        void main()
        {

            vec3 color = texture(diffuseMap, vec3(vUv, textureNumber)).rgb;
            colorFinish = vec4(color, 1);


        }
    `,
  side: THREE.DoubleSide,
  transparent: true,
  opacity: 0.005,
  uniforms: {
    uFrequency: { value: new THREE.Vector2(10, 5) },
    uTime: { value: 0 },
    uColor: { value: new THREE.Color("lightblue") },
    textureNumber: {value:0},
    diffuseMap: {},
  },

});

Have you tested with your textureLoader whether you can reach the textures with your texture paths?

Here’s a working example

Example : https://jsfiddle.net/seanwasere/6wo4m0sx/

image