Làm thế nào tôi có thể thực hiện chiếu sáng trong động cơ voxel?


15

Tôi đang tạo MC giống như động cơ địa hình và tôi đã nghĩ rằng ánh sáng sẽ khiến nó trông đẹp hơn rất nhiều. Vấn đề là các khối không được chiếu sáng đúng cách khi một khối phát ra ánh sáng (xem ảnh chụp màn hình ở phía dưới trên trang.

Cho đến nay tôi muốn thực hiện ánh sáng "khối" của minecraft. Vì vậy, tôi đã tạo ra một 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;
        }
    }

Tôi đoán nếu tôi muốn thực hiện chiếu sáng, tôi phải chỉ định ánh sáng cho mỗi đỉnh ... Và bây giờ trong tệp hiệu ứng của tôi, tôi muốn có thể lấy giá trị đó và thắp sáng đỉnh tương ứng:

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();
    }
}

Và đây là một ví dụ về cách tôi áp dụng ánh sáng:

            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);
                break;

Và cuối cùng ở đây là algorythim tính toán tất cả:

    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);
            Blocks.Add(location);

            // 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;
    }

Bất kỳ đề xuất và giúp đỡ sẽ được nhiều đánh giá cao

SCREENSHOTS Lưu ý các vật phẩm trên đỉnh trên địa hình và làm thế nào chỉ phần bên trái được chiếu sáng ... Cố gắng chiếu sáng 1 Vì một số lý do, chỉ một số mặt của khối lập phương được thắp sáng và nó không chiếu sáng mặt đất Cố gắng chiếu sáng 2

Một ví dụ khác về trước

Tìm ra vấn đề của tôi! Tôi đã không kiểm tra xem khối đó đã sáng chưa và nếu ở mức độ nào (nếu nó thấp hơn thì nó sẽ cao hơn)

    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)
        {
            light--;

            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);
        }
    }

Rõ ràng các công việc trên, có ai biết làm thế nào tôi sẽ làm cho nó hiệu quả hơn?



Một câu hỏi hay, nhưng đó là một bản sao của một số câu hỏi hiện có ... gamedev.stackexchange.com/questions/6507/ trên gamedev.stackexchange.com/questions/19207/iêu
Tim Holt

Câu trả lời:


17

Tôi đã thực hiện một cái gì đó tương tự như thế này. Tôi đã viết một bài về nó trên blog của mình: byte56.com/2011/06/a-light-post . Nhưng tôi sẽ đi vào chi tiết hơn một chút ở đây.

Trong khi bài báo codeflow liên kết trong một câu trả lời khác là khá thú vị. Theo những gì tôi hiểu, đó không phải là cách Minecraft chiếu sáng. Ánh sáng Minecraft là automata di động hơn sau đó là nguồn ánh sáng truyền thống.

Tôi giả sử bạn quen thuộc với dòng nước trong MC. Ánh sáng trong MC về cơ bản là giống nhau. Tôi sẽ dẫn bạn qua một ví dụ đơn giản.

Dưới đây là một vài điều cần ghi nhớ.

  • Chúng tôi sẽ giữ một danh sách các hình khối cần kiểm tra giá trị ánh sáng của chúng
  • Chỉ các hình khối trong suốt và hình khối phát sáng có giá trị ánh sáng

Khối đầu tiên chúng ta thêm là nguồn sáng. Một nguồn là một trường hợp đặc biệt. Giá trị ánh sáng của nó được đặt tương ứng với loại nguồn sáng (ví dụ: đèn khò có giá trị sáng hơn dung nham). Nếu một khối có giá trị ánh sáng được đặt trên 0, chúng ta thêm tất cả các khối trong suốt liền kề với khối đó vào danh sách. Đối với mỗi khối trong danh sách, chúng tôi đặt giá trị ánh sáng của nó thành hàng xóm sáng nhất trừ đi một khối. Điều này có nghĩa là tất cả các hình khối trong suốt (bao gồm cả "không khí") bên cạnh nguồn sáng có giá trị ánh sáng là 15. Chúng tôi tiếp tục đi bộ các khối xung quanh nguồn sáng, thêm các hình khối cần được kiểm tra và đưa các khối sáng ra khỏi danh sách , sử dụng chúng tôi không còn có bất kỳ để thêm. Điều đó có nghĩa là tất cả các giá trị mới nhất được đặt thành 0, có nghĩa là chúng ta đã đạt đến điểm cuối của ánh sáng.

Đó là một lời giải thích khá đơn giản về ánh sáng. Tôi đã làm một cái gì đó cao cấp hơn một chút, nhưng tôi đã bắt đầu với cùng một nguyên tắc cơ bản. Đây là một ví dụ về những gì nó tạo ra:

nhập mô tả hình ảnh ở đây

Bây giờ bạn có tất cả các tập dữ liệu ánh sáng của bạn. Khi bạn xây dựng các giá trị màu cho các đỉnh của mình, bạn có thể tham chiếu dữ liệu độ sáng này. Bạn có thể làm một cái gì đó như thế này (trong đó ánh sáng là giá trị int trong khoảng từ 0 đến 15):

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

Về cơ bản, tôi lấy giá trị ánh sáng từ 0 đến 1 với công suất 1,4f. Điều này mang lại cho tôi một bóng tối hơi tối hơn một hàm tuyến tính. Đảm bảo rằng giá trị màu của bạn không bao giờ vượt quá 1. Tôi đã làm điều đó bằng cách chia cho 16, thay vì 15 để tôi luôn có thêm một chút phòng. Sau đó di chuyển thêm vào cơ sở để tôi luôn có một chút kết cấu và không phải là màu đen thuần túy.

Sau đó, trong shader của tôi (tương tự như tập tin hiệu ứng), tôi lấy màu mảnh cho kết cấu và nhân nó với màu ánh sáng tôi tạo ở trên. Điều này có nghĩa là độ sáng đầy đủ mang lại kết cấu như được tạo ra. Độ sáng rất thấp cho kết cấu như rất tối (nhưng không phải màu đen vì màu cơ bản).

BIÊN TẬP

Để có được ánh sáng cho khuôn mặt, bạn nhìn vào khối lập phương theo hướng bình thường của khuôn mặt. Ví dụ, mặt trên của khối lập phương lấy dữ liệu ánh sáng từ khối trên.

CHỈNH SỬA 2

Tôi sẽ cố gắng giải quyết một số câu hỏi của bạn.

Vì vậy, những gì tôi sẽ làm là một cái gì đó như đệ quy trong trường hợp này?

Bạn có thể sử dụng thuật toán đệ quy hoặc lặp. Tùy bạn, bạn muốn thực hiện nó như thế nào. Chỉ cần đảm bảo rằng bạn đang theo dõi những hình khối nào đã được thêm vào, nếu không bạn sẽ tiếp tục mãi mãi.

Ngoài ra, thuật toán "ánh sáng đi xuống" như thế nào?

Nếu bạn đang nói về ánh sáng mặt trời, ánh sáng mặt trời hơi khác một chút, vì chúng ta không muốn nó giảm độ sáng. Dữ liệu khối của tôi bao gồm một bit SKY. Nếu một khối được đánh dấu là SKY có nghĩa là nó có quyền truy cập rõ ràng vào bầu trời mở phía trên nó. Các khối SKY luôn nhận được ánh sáng đầy đủ trừ đi mức độ tối. Sau đó, các hình khối bên cạnh bầu trời, không phải là bầu trời, như lối vào hang động hoặc nhô ra, thủ tục chiếu sáng bình thường sẽ diễn ra. Nếu bạn chỉ nói về một điểm sáng chiếu xuống ... thì nó cũng giống như bất kỳ hướng nào khác.

Làm thế nào tôi chỉ định ánh sáng cho một khuôn mặt duy nhất?

Bạn không chỉ định ánh sáng cho một mặt duy nhất. Mỗi khối lập phương trong suốt chỉ định ánh sáng cho tất cả các khối rắn có chung một mặt với nó. Nếu bạn muốn lấy ánh sáng cho khuôn mặt, chỉ cần kiểm tra giá trị ánh sáng cho khối trong suốt mà nó chạm vào. Nếu nó không chạm vào một khối trong suốt, thì dù sao bạn cũng sẽ không hiển thị nó.

Mẫu mã?

Không.


@ Byte56 Rất muốn biết cách dịch thuật toán này để hoạt động trên các "khối".
gopgop

Tôi nghĩ rằng chỉ cần cập nhật mỗi bên của khối theo hàng xóm và sau đó thêm tất cả các khối đã thay đổi vào danh sách các khối để thay đổi nhưng dường như nó không hoạt động
gopgop

@gopgop Các ý kiến ​​không phải là nơi để thảo luận. Bạn có thể tìm thấy tôi trong trò chuyện một thời gian. Hoặc thảo luận với những người khác ở đó.
MichaelHouse

4

Đọc qua bài viết sau vì nó sẽ cung cấp cho bạn rất nhiều thông tin về những gì bạn tìm kiếm. Có nhiều phần về ánh sáng, nhưng đặc biệt là đọc các phần về Ambient Ocinating, Observer Light và Light Gathering:

http://codeflow.org/entries/2010/dec/09/minecraft-like-rendering-experiment-in-opengl-4/

Nhưng cố gắng trả lời cốt lõi của câu hỏi của bạn:

  • Nếu bạn muốn làm tối một màu, hãy nhân nó với một màu khác (hoặc đơn giản là nổi giữa 0 và 1 nếu bạn muốn làm tối tất cả các thành phần bằng nhau), trong đó 1 trong một thành phần tượng trưng cho cường độ đầy đủ trong khi 0 là tối hoàn toàn.
  • Nếu bạn muốn làm sáng một màu, thêm màu khác vào đó và kẹp kết quả. Nhưng hãy cẩn thận không chọn các giá trị quá cao sẽ làm cho kết quả bão hòa thành màu trắng tinh khiết. Chọn màu tối, với một chút màu sắc mà bạn đang tìm kiếm, chẳng hạn như màu cam đậm (# 886600).

    hoặc là...

    Sau khi thêm màu, thay vì kẹp kết quả (nghĩa là kẹp mọi thành phần của màu từ 0 đến 1), thay đổi tỷ lệ kết quả - tìm phần nào trong ba thành phần (RGB) là lớn nhất và chia tất cả chúng cho giá trị đó. Phương pháp này bảo tồn các thuộc tính ban đầu của màu sắc tốt hơn một chút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.