Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am creating the MC like terrain engine, and I have thought that lighting would make it look a whole lot nicer.The problem is that the blocks are not being lit properly when a block which emits light is placed (see the screenshots at the bottom on the page.

So far I want to implement minecraft's "blocky" lighting. So I created a VertexFormat:

 struct VertexPositionTextureLight
        Vector3 position;
        Vector2 textureCoordinates;
        float light;

        public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
            new VertexElement(sizeof(float) * 5, VertexElementFormat.Single, VertexElementUsage.TextureCoordinate, 1)

        public VertexPositionTextureLight(Vector3 position, Vector3 normal, Vector2 textureCoordinate, float light)
            // I don't know why I included normal data :)
            this.position = position;
            this.textureCoordinates = textureCoordinate;
            this.light = light;

I guess if I want to implement lighting I have to specify a light for each vertex... And now in my effect file I want to be able to take that value and light up the vertex accordingly:

float4x4 World;
float4x4 Projection;
float4x4 View;

Texture Texture;

sampler2D textureSampler = sampler_state  {
    Texture = <Texture>;
    MipFilter = Point;
    MagFilter = Point;
    MinFilter = Point;
    AddressU = Wrap;
    AddressV = Wrap;

struct VertexToPixel  {
    float4 Position     : POSITION;
    float4 TexCoords    : TEXCOORD0;
    float4 Light        : TEXCOORD01;

struct PixelToFrame  {
    float4 Color        : COLOR0;

VertexToPixel VertexShaderFunction(float4 inPosition : POSITION, float4 inTexCoords : TEXCOORD0, float4 light : TEXCOORD01)  {
    VertexToPixel Output = (VertexToPixel)0;

    float4 worldPos = mul(inPosition, World);
    float4 viewPos = mul(worldPos, View);

    Output.Position = mul(viewPos, Projection);
    Output.TexCoords = inTexCoords;
    Output.Light = light;

    return Output;

PixelToFrame PixelShaderFunction(VertexToPixel PSIn)  {
    PixelToFrame Output = (PixelToFrame)0;

    float4 baseColor = 0.086f;
    float4 textureColor = tex2D(textureSampler, PSIn.TexCoords);
    float4 colorValue = pow(PSIn.Light / 16.0f, 1.4f) + baseColor;

    Output.Color = textureColor;

    Output.Color.r *= colorValue;
    Output.Color.g *= colorValue;
    Output.Color.b *= colorValue;
    Output.Color.a = 1;

    return Output;

technique Block  {
    pass Pass0  {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();

VertexToPixel VertexShaderBasic(float4 inPosition : POSITION, float4 inTexCoords : TEXCOORD0)  {
    VertexToPixel Output = (VertexToPixel)0;

    float4 worldPos = mul(inPosition, World);
    float4 viewPos = mul(worldPos, View);

    Output.Position = mul(viewPos, Projection);
    Output.TexCoords = inTexCoords;

    return Output;

PixelToFrame PixelShaderBasic(VertexToPixel PSIn)  {
    PixelToFrame Output = (PixelToFrame)0;

    Output.Color = tex2D(textureSampler, PSIn.TexCoords);

    return Output;

technique Basic  {
    pass Pass0  {
        VertexShader = compile vs_2_0 VertexShaderBasic();
        PixelShader = compile ps_2_0 PixelShaderBasic();

And this is an example on how i apply lighting:

            case BlockFaceDirection.ZDecreasing:
                light = world.GetLight((int)(backNormal.X + pos.X), (int)(backNormal.Y + pos.Y), (int)(backNormal.Z + pos.Z));

                SolidVertices.Add(new VertexPositionTextureLight(bottomRightBack, backNormal, bottomLeft, light));
                SolidVertices.Add(new VertexPositionTextureLight(bottomLeftBack, backNormal, bottomRight, light));
                SolidVertices.Add(new VertexPositionTextureLight(topRightBack, backNormal, topLeft, light));
                SolidVertices.Add(new VertexPositionTextureLight(topLeftBack, backNormal, topRight, light));
                AddIndices(0, 2, 3, 3, 1, 0);

And last of all here is the algorythim that calculates it all:

    public void AddCubes(Vector3 location, float light)
        AddAdjacentCubes(location, light);
        Blocks = new List<Vector3>();

    public void Update(World world)
        this.world = world;

    public void AddAdjacentCubes(Vector3 location, float light)
        if (light > 0 && !CubeAdded(location))
            world.SetLight((int)location.X, (int)location.Y, (int)location.Z, (int)light);

            // Check ajacent cubes
            for (int x = -1; x <= 1; x++)
                for (int y = -1; y <= 1; y++)
                    for (int z = -1; z <= 1; z++)
                        // Make sure the cube checked it not the centre one
                        if (!(x == 0 && y == 0 && z == 0))
                            Vector3 abs_location = new Vector3((int)location.X + x, (int)location.Y + y, (int)location.Z + z);

                            // Light travels on transparent block ie not solid
                            if (!world.GetBlock((int)location.X + x, (int)location.Y + y, (int)location.Z + z).IsSolid)
                                AddAdjacentCubes(abs_location, light - 1);


    public bool CubeAdded(Vector3 location)
        for (int i = 0; i < Blocks.Count; i++)
            if (location.X == Blocks[i].X &&
                location.Y == Blocks[i].Y &&
                location.Z == Blocks[i].Z)
                return true;

        return false;

Any suggestions and help would be much appreciated

SCREENSHOTS Notice the artifacts on the top on the terrain and how only the left part is partically lit... Attempt at lighting 1 For some reason only certain sides of the cube is being lit and it doesn't light the ground Attempt at lighting 2

Another example of the previous

Figured out my problem! I was not checking if that block was already lit and if so to what degree (if it is lower light it higher)

    public void DoLight(int x, int y, int z, float light)
        Vector3 xDecreasing = new Vector3(x - 1, y, z);
        Vector3 xIncreasing = new Vector3(x + 1, y, z);
        Vector3 yDecreasing = new Vector3(x, y - 1, z);
        Vector3 yIncreasing = new Vector3(x, y + 1, z);
        Vector3 zDecreasing = new Vector3(x, y, z - 1);
        Vector3 zIncreasing = new Vector3(x, y, z + 1);

        if (light > 0)

            world.SetLight(x, y, z, (int)light);
            Blocks.Add(new Vector3(x, y, z));

            if (world.GetLight((int)yDecreasing.X, (int)yDecreasing.Y, (int)yDecreasing.Z) < light &&
                world.GetBlock((int)yDecreasing.X, (int)yDecreasing.Y, (int)yDecreasing.Z).BlockType == BlockType.none)
                DoLight(x, y - 1, z, light);
            if (world.GetLight((int)yIncreasing.X, (int)yIncreasing.Y, (int)yIncreasing.Z) < light &&
                world.GetBlock((int)yIncreasing.X, (int)yIncreasing.Y, (int)yIncreasing.Z).BlockType == BlockType.none)
                DoLight(x, y + 1, z, light);
            if (world.GetLight((int)xDecreasing.X, (int)xDecreasing.Y, (int)xDecreasing.Z) < light &&
                world.GetBlock((int)xDecreasing.X, (int)xDecreasing.Y, (int)xDecreasing.Z).BlockType == BlockType.none)
                DoLight(x - 1, y, z, light);
            if (world.GetLight((int)xIncreasing.X, (int)xIncreasing.Y, (int)xIncreasing.Z) < light &&
                world.GetBlock((int)xIncreasing.X, (int)xIncreasing.Y, (int)xIncreasing.Z).BlockType == BlockType.none)
                DoLight(x + 1, y, z, light);
            if (world.GetLight((int)zDecreasing.X, (int)zDecreasing.Y, (int)zDecreasing.Z) < light &&
                world.GetBlock((int)zDecreasing.X, (int)zDecreasing.Y, (int)zDecreasing.Z).BlockType == BlockType.none)
                DoLight(x, y, z - 1, light);
            if (world.GetLight((int)zIncreasing.X, (int)zIncreasing.Y, (int)zIncreasing.Z) < light &&
                world.GetBlock((int)zIncreasing.X, (int)zIncreasing.Y, (int)zIncreasing.Z).BlockType == BlockType.none)
                DoLight(x, y, z + 1, light);

Altough the above works, would anyone know how I would make it more performace efficent?

share|improve this question

migrated from stackoverflow.com Dec 18 '11 at 13:53

This question came from our site for professional and enthusiast programmers.

A fine question, but it's a duplicate of several existing questions... gamedev.stackexchange.com/questions/6507/… gamedev.stackexchange.com/questions/19207/… –  Tim Holt Dec 19 '11 at 0:53

2 Answers 2

up vote 15 down vote accepted

I've implemented something similar to this. I wrote up a post about it on my blog: byte56.com/2011/06/a-light-post. But I'll go into a little more detail here.

While the codeflow article linked in another answer is pretty interesting. From what I understand, it's not how Minecraft does its lighting. Minecraft lighting is more cellular automata then traditional light source.

I assume you're familiar with water flow in MC. Lighting in MC is essentially the same thing. I'll walk you through a simple example.

Here are a few things to keep in mind.

  • We're going to keep a list of cubes that need their lighting values checked
  • Only transparent cubes and light emitting cubes have lighting values

The first cube we add is the light source. A source is a special case. It's light value is set accordingly to the light source type (for example torches get a brighter value than lava). If a cube has its light value set above 0, we add all the transparent cubes adjacent to that cube to the list. For each cube on the list, we set its light value to its brightest neighbor minus one. This means that all the transparent cubes (this includes "air") next to the light source get a light value of 15. We continue walking the cubes around the light source, adding cubes that need to be checked and taking lit cubes off the list, util we no longer have any to add. That means that all the latest values set have been set to 0, which means we've reached the end of our light.

That's a fairly simple explanation of lighting. I've done something a little more advanced, but I started with the same basic principle. This is an example of what it produces:

enter image description here

Now that you have all your light data set. When you're building the color values for your vertices, you can reference this brightness data. You could do something like this (where light is an int value between 0 and 15):

float baseColor = .086f;
float colorValue = (float) (Math.pow(light / 16f, 1.4f) + baseColor );
return new Color(colorValue, colorValue, colorValue, 1);

Basically I'm taking the light value from 0 to 1 to the power of 1.4f. This gives me a slightly darker darks than a linear function. Ensure that your color value never goes above 1. I did that by dividing by 16, instead of 15 so I'd always have a little extra room. Then moved that extra to the base so I'd always have a little texture and not pure blackness.

Then in my shader (similar to an effects file), I get the fragment color for the texture and multiply that by the lighting color I create above. This means full brightness gives the texture as it was created. Very low brightness gives the texture as very dark (but not black because of the base color).


To get the light for a face, you look at the cube in the direction of the normal of the face. For example, top face of cube gets the light data from the cube above.


I'll attempt to address some of your questions.

So what I would do is something like recursion in this case?

You can use a recursive algorithm or iterative. It's up you you how you want to implement it. Just ensure you're keeping track of which cubes have been added already, otherwise you'll keep going forever.

Also how would the algorithm "light downwards"?

If you're talking about sunlight, sunlight is a bit different, since we don't want it to decrease in brightness. My cube data includes a SKY bit. If a cube is marked as SKY that means it has clear access to the open sky above it. SKY cubes always get full lighting minus the darkness level. Then cubes next to sky, that are not sky, like cave entrances or overhangs, the normal lighting procedure takes over. If you're just talking about a point light shining down... it's the same as any other direction.

How would I specify the light for a single face only?

You don't specify light for a single face only. Each transparent cube specifies the light for all the solid cubes that share a face with it. If you want to get the light for a face, simply check the light value for the transparent cube it's touching. If it's not touching a transparent cube, then you wouldn't be rendering it anyway.

Code samples?


share|improve this answer
Pretty interesting. But if full brightness results in the original texture color, how do you handle cells that should be really bright like lava? And the impression I get from the screenshot is that the entire world is really dark. Looks pretty cool anyway. –  David Gouveia Dec 18 '11 at 19:36
Yes, it looks dark because it's a night scene with a single light source. You can see what daylight looks like here. When creating your textures, you create them to look like they should when they are at maximum brightness. –  Byte56 Dec 18 '11 at 20:03
@Darestium As David Gouveia said, you'll have to multiply the color, not add. As for if your code is "acceptable", that's up to you :). Does the code work as you want it to? That being said, it looks like it may run forever, since I don't see where you keep track of cubes you've already added. You'll have neighbors adding each other back and forth. –  Byte56 Dec 19 '11 at 1:44
Well, it could be that the == operator is checking that the Vector3 objects are the same object. You want to check to see if they identify the same world location. I would strongly recommend putting a break point in your CubeAdded function. You need to learn debugging skills if you're to continue in programming. Step through the code, check the values manually. I can't stress that enough. Debugging skills are critical. –  Byte56 Dec 19 '11 at 3:52
Looks like you're missing some cubes when you're looping. I think you're on your own for that one. You'll have to step through and find out why so many cubes are being left out. Perhaps try a iterative approach? Good luck! –  Byte56 Dec 19 '11 at 7:05

Read through the following article as it should give you a lot of information on what you seek. There are many sections about lighting, but in particular read the sections about Ambient Occlusion, Observer Light and Light Gathering:


But trying to answer the core of your question:

  • If you want to darken a color, multiply it by another color (or simply by a float between 0 and 1 if you want to darken all components equally), where 1 in a component stands for full intensity while 0 stands for full darkness.
  • If you want to lighten a color, add another color to it and clamp the result. But be careful not to choose values that are so high that it would make the result saturate to pure white. Choose dark colors, with an hint of the hue that you're looking for, such as a dark orange (#886600).


    After adding the color, instead of clamping the result (i.e. clamping every component of the color between 0 and 1), scale the result instead - find which of the three components (RGB) is the largest and divide all of them by that value. This method preserves the original properties of the color a bit better.

share|improve this answer
Many Thanks! :) –  Darestium Dec 19 '11 at 22:06

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.