Drawing Specific Pixels of a Texture onto a Sprite

Is it possible to draw lets say the top-left 100x100 pixels of a PNG onto a Sprite?

I’ve been looking for this feature for years and I’m not sure if it is possible yet, or if we are still limited to UV mapping.

yes. you can set the texture.repeat, and texture.offset to display a certain part of the texture.

texture.wrapS=texture.wrapT = THREE.RepeatWrapping; //enable repeat wrapping
texture.repeat.set(.5,.5); //Tile the text half a time
texture.offset.set(.25,.25); //Change the origin a bit
1 Like

Make the rest transparent

Yes that’s how the UV mapping works. But I need to draw specific pixels, not a part of the texture.

“draw lets say the top-left 100x100”

you can do this with texture.repeat and texture.offset no ?

(repeat and offset are multipliers and offsets for the uv mapping)

If you mean like… only certain pixels by color or something… then you either have to make a new texture, or use a customized shader / material.

In a sprite sheet that have different sizes, you can not.

This is basically what I need:
https://doc.babylonjs.com/features/featuresDeepDive/sprites/packed_manager

I think @manthrax has already explained it. You can cut any rectangle from a texture and use it in a sprite. You can have different sprites use different rectangles (incl. different sizes) from the same texture.

1 Like

And .rotation! too!

1 Like

Yes, you are right. It is possible to convert pixels to UV coordinates through some calculations. However, the problem is a bit more complex for what I want to achieve. In my case, I want to create a sprite animation, and for that, I exported the spritesheet using TexturePacker. The result of my spritesheet is this:

And the output of the JSON is something like this:

{
	"size": {
		"w": 1023,
		"h": 754
	},
	"sprites": [
		{
			"filename": "axeman_blue_c_0116.png",
			"region": {
				"x": 1,
				"y": 1,
				"w": 199,
				"h": 93
			},
			"margin": {
				"x": 113,
				"y": 254,
				"w": 313,
				"h": 419
			}
		},
		...

Since we cannot add margins or pivot points, it is not possible to achieve this in Three.js. TexturePacker supports over 48+ game engines, but Three.js is not one of them for this reason. I believe adding such a feature would be very powerful for anyone working extensively with sprites.

You keep saying “not possible” but it’s totally possible.
You can do it in bare threejs js, or you can do it in a shader.

Here’s some sprites rendered from a spritesheet:
https://vectorslave.com/terrain-grid/index.html
(They aren’t actually THREE Sprites, they are written directly to a single vertex buffer for performance, but they could be Sprites or what not)

Now if you mean that there is no build in class that interacts with texturepackers json format, that is correct. But it shouldn’t be to hard make one.

Would you like some help with that?

2 Likes

Texturepacker. Maybe you need for const size of sprites instead of pivot.


indicator

function indicator_atlas_set(){
atlas["indicator"]=[512,256];
atlas["indicator_ammo"]=[512/44,256/44,2/512,1-(134+44)/256];
atlas_data["indicator_ammo"]=[512,256,44,44,134];
atlas["indicator_area_a"]=[512/44,256/44,2/512,1-(182+44)/256];
atlas_data["indicator_area_a"]=[512,256,44,44,182];
atlas["indicator_area_b"]=[512/44,256/44,50/512,1-(134+44)/256];
atlas_data["indicator_area_b"]=[512,256,44,44,134];
atlas["indicator_area_c"]=[512/44,256/44,50/512,1-(182+44)/256];
atlas_data["indicator_area_c"]=[512,256,44,44,182];
atlas["indicator_area_d"]=[512/44,256/44,98/512,1-(134+44)/256];
atlas_data["indicator_area_d"]=[512,256,44,44,134];
atlas["indicator_arrow"]=[512/16,256/16,230/512,1-(2+16)/256];
atlas_data["indicator_arrow"]=[512,256,16,16,2];
atlas["indicator_coin"]=[512/128,256/128,2/512,1-(2+128)/256];
atlas_data["indicator_coin"]=[512,256,128,128,2];
atlas["indicator_color"]=[512/10,256/10,146/512,1-(2+10)/256];
atlas_data["indicator_color"]=[512,256,10,10,2];
atlas["indicator_damage"]=[512/128,256/32,98/512,1-(182+32)/256];
atlas_data["indicator_damage"]=[512,256,128,32,182];
atlas["indicator_digits_big"]=[512/160,256/16,262/512,1-(2+16)/256];
atlas_data["indicator_digits_big"]=[512,256,160,16,2];
atlas["indicator_digits_distance"]=[512/256,256/16,2/512,1-(230+16)/256];
atlas_data["indicator_digits_distance"]=[512,256,256,16,230];
}

I think it is not possible, but correct me if I am wrong. To achieve the padding/margin/offset/pivot-point, however you want to call it, it should be done by adding the Sprite inside a Group and then transforming the geometry. I tried this technique, but it interferes with the depth order of the other Objects. This is because the padding is being done at the level of space-geometry and not at the level of the pixels-textures.

It looks like you already believe this is not possible. No argument can overpower this.

Out of curiosity, what are “padding/margin/offset/pivot-point”? Could you make an illustration (hand-drawn is OK) of a sprite in packed texture, and the same sprite on screen, so that it is easy to understand all these parameters.

I still do think it is possible – my argument is that it is already done in other systems, so it can be done in JavaScript and Three.js. Sometimes people confuse “I cannot do it” with “It cannot be done”. They are completely different statements.

images

Anyway, good luck with your project. I hope you will find a solution to your sprite issues.

2 Likes

I dont know why people do so hard solutions instead of simple in genius like into games

My original frame is 512x512. The red square is the sprite that I want to draw (199x93). And the blue intersection is the padding/margin I need to apply (113x254).

The atlas you provided as example looks like what I need. Would you mind to explain how you do it?

3 years ago i wrote exporter for texturepacker. I dont remember how it works
Three.js.zip (3.4 KB)
I am using custom shader to show 2d instanced sprites through ortho camera

vs["indicator"]=`
attribute vec2 offset;
attribute vec2 scale;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute vec4 frame;
attribute float texture;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying vec4 vFrame;
varying float tex_num;
void main(){
float angle=rotation;
vec3 vPosition=vec3(position.x*scale.x*cos(angle)-position.y*scale.y*sin(angle),position.y*scale.y*cos(angle)+position.x*scale.x*sin(angle),position.z);
vPosition.x+=offset.x;
vPosition.y+=offset.y;
vUv=uv;
vColor=color;
vBlend=blend;
tex_num=texture;
vFrame=frame;
gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition,1.0);
}
`;
fs["indicator"]=`
const int count=1;
uniform sampler2D map[count];
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying vec4 vFrame;
varying float tex_num;
void main(){
if(tex_num==0.0){
gl_FragColor=texture2D(map[0],vUv/vFrame.xy+vFrame.zw);
}
gl_FragColor*=vColor;
gl_FragColor.rgb*=gl_FragColor.a; 
gl_FragColor.a*=vBlend;
}
`;
let geometry=new THREE.InstancedBufferGeometry();
geometry.setAttribute('position',new THREE.Float32BufferAttribute(new Float32Array([-0.5,0.5,0,-0.5,-0.5,0,0.5,0.5,0,0.5,-0.5,0,0.5,0.5,0,-0.5,-0.5,0]),3));
geometry.setAttribute('uv',new THREE.Float32BufferAttribute(new Float32Array([0,1,0,0,1,1,1,0,1,1,0,0]),2));
geometry.setAttribute('offset',new THREE.InstancedBufferAttribute(new Float32Array(),2));
geometry.setAttribute('scale',new THREE.InstancedBufferAttribute(new Float32Array(),2));
geometry.setAttribute('rotation',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('color',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('blend',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('frame',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('texture',new THREE.InstancedBufferAttribute(new Float32Array(),1));


mat["indicator"]=new THREE.ShaderMaterial({
uniforms:{
map:{value:[tex["indicator"]]},
},
vertexShader:vs["indicator"],
fragmentShader:fs["indicator"],
transparent:true,
depthTest:false,
depthWrite:false,
blending:THREE.CustomBlending,
blendEquation:THREE.AddEquation,
blendSrc:THREE.OneFactor,
blendDst:THREE.OneMinusSrcAlphaFactor
});
mesh["indicator"]=new THREE.Mesh(geometry,mat["indicator"]);
mesh["indicator"].frustumCulled=false;
mesh["indicator"].matrixAutoUpdate=false;
mesh["indicator"].updateMatrixWorld=function(){};
scene_hud.add(mesh["indicator"]);
1 Like