3D VoxelArt Character

Soooo … I’m making a 3D game (Should be some kind of MOBA KitPvP but for the art that’s irrelevant :smiley: )

The problem is: I’m totally too bad on making good Character Models.
The whole Game should be Voxel-Based (to make it easier to generate a limited random world the players can interact with, e.q. Cutting down Trees, collect Berries etc.)
Creating some Example Trees is quite easy, getting generated by the Server randomly (Same with Bushes for Berries etc.)
But for Character Models that won’t work (For Monsters or Animals that run arround neither) so i made myself an editor that lets me create Voxel Models and export in a format that saves space and lets me load them very simple (Into VBOs). There’s 2 Files the Editor produces: the Geometry Data containing each Voxel’s Position and Color (For Modelloading and -Editing purpose) , and the File that gets used for loading the Model, it just contains the Color, Normals and Vertices for only the faces that are Visible, so Cubes inside of a Model get Lost in this Format.

So well, Can anyone show me a good Tutorial for 3D Voxel Characters that don’t have too many vertices? or Any ideas how to Improve Mine cause i think it doesn’t look too good: ???

It looks good…

What, really? i mean look at Cube Worlds Voxel models:

https://d1bcl7tdsf48aa.cloudfront.net/images/avatars/rogue-elf-m2-big.jpg

or this one over here which is just an example from a voxel editor i found online:

The one from cubeworld deffinitly needs more Power to render cause it has much more voxels in it, but the second one looks very good in my opinion and has arround the same amout of cubes my model has.

Just reproduced the second Character and well … it doesn’t look that good in my program :clue: the hair looks very strange cause the faces that point in the same direction have exactly the same color, in the editor it’s not as you can see in my post above, so it’s deffinitly the shaders which make my models look less good :-X any tutorial on how to improve that?

CubeWorld avatar > your model > … > … > the example you found.
Seriously, go that way and it will be fine.

Oh okay, so it’s just me thinking that all the Art (if 2D or 3D) i make is worse than everyone else’s stuff? :o

It’s probably because your program doesn’t use any kind of lighting/shading and anti aliasing.
However, I would suggest you not to work too much on the tiny details of the tool, but rather spend the time developing the game itself. As long as you can get the job done with the tool it’s fine and helpful, but spending days trying to make your models look better in your voxel editor is rather counterintuitive. :slight_smile:

Well i don’t want them to look good in the model editor, i want them to look good in the game (might as well switch to the modeleditor I got that image from cause it has much more functions) but in my game they look less good and that’s what disappoints me, cause it does not look the way i want it to :slight_smile:

Tryed the model i made myself out in the editor with their shaders and I think it looks way better, So for now i’ll focus on making the actual game and make better Shaders for it later :slight_smile:

When/if you get to shaders, look into per pixel lighting, ambient occlusion, and a specular component. That should make it look more like the cube world photo.

Okay, I’ll start with the Ambient Occlusion cause that’s what i think will help me the most, next I’ll go with per pixel lighting and the the specular component. That’s all in the Fragment Shader isn’t it? cause it doesn’t apply to the whole face i guess

Can someone help me with the Point Light per pixel? :confused:

the only thing not working is placing the Light source. as you can see i set the position to a very high y amount so basically it shouldn’t be visible at all but in the 3D room, the light comes from the Point(0|0|0)

Vertex Shader:

varying vec4 pos;

void main()
{
	gl_TexCoord[0] = gl_MultiTexCoord0;
	
	pos = gl_Vertex;
	
	gl_Position = ftransform();
	
	vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
	
	vec4 mul = vec4(normal.z,normal.z,normal.z,1.0);
	
	gl_FrontColor = gl_Color * mul;
}

Fragment Shader:



uniform sampler2D tex;
// Lights
uniform vec3 lpos[100];
uniform vec3 color[100];
uniform float intense[100];
uniform int amount;
uniform float alpha[100];

varying vec4 pos;

void main()
{	
	int i;
	vec4 finalcolor = texture2D(tex,gl_TexCoord[0].st)*gl_Color;
	for(i = 0; i < amount; i++){
		// distance
		vec3 ppos = vec3(pos.x,pos.y,pos.z);
		float distance = length(lpos[i]-ppos);
		
		if(distance < intense[i]){
			float falpha = 1.0-(distance/intense[i]);
			finalcolor.r = finalcolor.r + color[i].r * falpha * alpha[i];
			finalcolor.g = finalcolor.g + color[i].g * falpha * alpha[i];
			finalcolor.b = finalcolor.b + color[i].b * falpha * alpha[i];
		}
	}
	gl_FragColor = finalcolor;
}

Rendering:

public void render() {
		cam.lookThrough();
		test.shader.addLight(new Vector3f(0, 1000000, 0),
				new Vector3f(1, 0, 0), 1f, 10);
		GL11.glColor4f(r, g, b, a);

		GL11.glBegin(GL11.GL_LINES);
		GL11.glVertex3f(-10f, 0, 0);
		GL11.glVertex3f(10f, 0, 0);
		GL11.glVertex3f(0, 0, -10f);
		GL11.glVertex3f(0, 0, 10f);
		GL11.glEnd();
		model.draw();
	}

Shader init:

package util.shader;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.GL20;
import org.lwjgl.util.vector.Vector3f;

public class Shader {
	public int program, vert, frag;

	public Shader(String path) throws Exception {
		// Load Vertex Shader
		vert = 0;
		vert = ARBShaderObjects
				.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);
		ARBShaderObjects.glShaderSourceARB(vert, readFileAsString(path
				+ ".vert"));
		ARBShaderObjects.glCompileShaderARB(vert);
		if (GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == 0) {
			System.err.println(GL20.glGetShaderInfoLog(vert, 1024));
			System.exit(1);
		}
		// Load Fragment Shader
		frag = 0;
		frag = ARBShaderObjects
				.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
		ARBShaderObjects.glShaderSourceARB(frag, readFileAsString(path
				+ ".frag"));
		ARBShaderObjects.glCompileShaderARB(frag);
		if (GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == 0) {
			System.err.println(GL20.glGetShaderInfoLog(frag, 1024));
			System.exit(1);
		}
		// Load Shader Program
		program = 0;
		program = ARBShaderObjects.glCreateProgramObjectARB();
		ARBShaderObjects.glAttachObjectARB(program, vert);
		ARBShaderObjects.glAttachObjectARB(program, frag);
		ARBShaderObjects.glLinkProgramARB(program);
		if (GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) == 0) {
			System.err.println(GL20.glGetProgramInfoLog(program, 1024));
			System.exit(1);
		}
		ARBShaderObjects.glValidateProgramARB(program);
		if (GL20.glGetProgrami(program, GL20.GL_VALIDATE_STATUS) == 0) {
			System.err.println(GL20.glGetProgramInfoLog(program, 1024));
			System.exit(1);
		}
	}

	public void activate() {
		ARBShaderObjects.glUseProgramObjectARB(program);
		light = 0;
	}

	public void deactivate() {
		ARBShaderObjects.glUseProgramObjectARB(0);
	}

	public void destroy() {
		ARBShaderObjects.glDeleteObjectARB(frag);
		ARBShaderObjects.glDeleteObjectARB(vert);
		ARBShaderObjects.glDeleteObjectARB(program);
	}

	private String readFileAsString(String filename) throws Exception {
		StringBuilder source = new StringBuilder();

		FileInputStream in = new FileInputStream(filename);

		Exception exception = null;

		BufferedReader reader;
		try {
			reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));

			Exception innerExc = null;
			try {
				String line;
				while ((line = reader.readLine()) != null)
					source.append(line).append('\n');
			} catch (Exception exc) {
				exception = exc;
			} finally {
				try {
					reader.close();
				} catch (Exception exc) {
					if (innerExc == null)
						innerExc = exc;
					else
						exc.printStackTrace();
				}
			}

			if (innerExc != null)
				throw innerExc;
		} catch (Exception exc) {
			exception = exc;
		} finally {
			try {
				in.close();
			} catch (Exception exc) {
				if (exception == null)
					exception = exc;
				else
					exc.printStackTrace();
			}

			if (exception != null)
				throw exception;
		}

		return source.toString();
	}

	// Tests
	public void setUniform(String uniform, float x, float y, float z) {
		int loc = GL20.glGetUniformLocation(program, uniform);
		GL20.glUniform3f(loc, x, y, z);
	}

	public void setUniform(String uniform, int x) {
		int loc = GL20.glGetUniformLocation(program, uniform);
		GL20.glUniform1i(loc, x);
	}

	public void setUniform(String uniform, float x) {
		int loc = GL20.glGetUniformLocation(program, uniform);
		GL20.glUniform1f(loc, x);
	}

	// Light? :/
	int light = 0;

	public void addLight(Vector3f pos, Vector3f col, float alpha, float intense) {
		if (light != 100) {
			setUniform("lpos[" + light + "]", pos.x, pos.y, pos.z);
			setUniform("color[" + light + "]", col.x, col.y, col.z);
			setUniform("intense[" + light + "]", intense);
			setUniform("alpha[" + light + "]", alpha);
			light++;
			setUniform("amount", light);
		}
	}
}

This won’t solve your problem, but there is room for improvement:

Use a [icode]varying vec3 col[/icode] to pass your calculated color to the fragment shader

use the [icode]varying vec3 col[/icode] here.

Okay, did that and also improved fragment shader:


		// distance
      vec3 ppos = vec3(pos.x,pos.y,pos.z);
      float distance = length(lpos[i]-ppos);

to:


		// distance
		float distance = length(lpos[i]-pos.xyz);

still i don’t understand why

lpos[i]

allways is 0,0,0
// had to put it in code tabs cause it writes italic there haha :stuck_out_tongue:


		finalcolor.r=lpos[i].x;
		finalcolor.g=lpos[i].y;
		finalcolor.b=lpos[i].z;

Since this colors the whole model black i guess it doesn’t change the lpos[i] at all and it stays 0,0,0 (it should be 0,1000000,0)

I’m so done, what did i do wrong? :confused:


			setUniform("color[" + light + "]", col.x, col.y, col.z); // works
																		// totally
																		// fine

they both get called just after each other, and i checked, the Vector3f pos has values in it right before and after calling this line:


			setUniform("lpos[" + light + "]", pos.x, pos.y, pos.z); // doesn't
																	// work at
																	// all

Current Vertex Shader:

varying vec4 pos;
varying vec4 col;
void main()
{
	gl_TexCoord[0] = gl_MultiTexCoord0;
	pos = ftransform();
	gl_Position = ftransform();
	vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
	vec4 mul = vec4(normal.z,normal.z,normal.z,1.0);
	col = gl_Color * mul;
}

Current Fragment Shader:

uniform sampler2D tex;
// Lights
uniform vec3 lpos[100];
uniform vec3 color[100];
uniform float intense[100];
uniform int amount;
uniform float alpha[100];

varying vec4 pos;
varying vec4 col;

void main()
{	
	int i;
	vec4 finalcolor = texture2D(tex,gl_TexCoord[0].st)*col;
	for(i = 0; i < amount; i++){
		// distance
		float distance = length(lpos[i]-pos.xyz);
		
		if(distance < intense[i]){
			float falpha = 1.0-(distance/intense[i]);
			finalcolor.r = finalcolor.r + color[i].r * falpha * alpha[i];
			finalcolor.g = finalcolor.g + color[i].g * falpha * alpha[i];
			finalcolor.b = finalcolor.b + color[i].b * falpha * alpha[i];
		}
	}
	gl_FragColor = finalcolor;
}

You set lpos[ i ].y to a massive value.
Therefore length(pos - lpos) is huge.
Therefore it is greater than ‘intense’ (10).

The if condition yields false, causing the contents of the if block never to be executed.

well. that’s what’s supposed to happen (for testing if i can set the position) but the light source is allways on the same place, if i set it to 0,0,0 or to 0,1000000,0 :confused: the light still appears at the point 0,0,0 even if it shouldn’t affect anything at all cause the y value is too big

Here that’s what shows up (white light) it’s the same if i place it in 0,0,0 or 0,1000000,0:

Change your frag.shader to:[icode]gl_FragColor = vec4(lpos[0].xyz, 1.0);[/icode]
and slowly increase lpos.y