ฉันจะติดตั้งไฟในเครื่องยนต์ voxel ได้อย่างไร


15

ฉันกำลังสร้าง MC เหมือนเครื่องยนต์ภูมิประเทศและฉันคิดว่าแสงจะทำให้มันดูดีขึ้นมากปัญหาคือบล็อกไม่สว่างอย่างเหมาะสมเมื่อวางบล็อกที่ปล่อยแสง (ดูภาพหน้าจอที่ด้านล่าง บนหน้า

จนถึงตอนนี้ฉันต้องการใช้แสง "บล็อก" ของ minecraft ดังนั้นฉันจึงสร้าง 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;
        }
    }

ฉันเดาว่าถ้าฉันต้องการใช้แสงฉันต้องระบุแสงสำหรับแต่ละจุดยอด ... และตอนนี้ในไฟล์เอฟเฟกต์ของฉันฉันต้องการที่จะสามารถใช้ค่านั้นและทำให้จุดยอดนั้นสว่างขึ้น:

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

และนี่คือตัวอย่างเกี่ยวกับวิธีการใช้แสง:

            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;

และสุดท้ายของทั้งหมดนี่คือ algorythim ที่คำนวณได้ทั้งหมด:

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

ข้อเสนอแนะและความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมมาก

SCREENSHOTS สัง เกตุสิ่งประดิษฐ์ที่อยู่ด้านบนของภูมิประเทศและส่วนที่เหลืออยู่เพียงแค่ส่องสว่างความพยายามในการให้แสงสว่าง 1 บางส่วน... ด้วยเหตุผลบางอย่างเท่านั้นที่บางด้านของก้อนจะถูกจุดและมันไม่สว่างพื้น พยายามส่องแสงสว่าง 2

อีกตัวอย่างหนึ่งจากก่อนหน้านี้

คิดออกปัญหาของฉัน! ฉันไม่ได้ตรวจสอบว่าบล็อกนั้นมีแสงสว่างอยู่หรือไม่และถ้าเป็นเช่นนั้นในระดับใด (ถ้าเป็นแสงที่ต่ำกว่าจะสูงกว่า)

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

เหนือสิ่งอื่นใดผลงานใครจะรู้ได้อย่างไรว่าฉันจะทำให้มันมีประสิทธิภาพที่ดีขึ้น



คำถามที่ดี แต่มันเป็นคำถามซ้ำกับที่มีอยู่หลาย ... gamedev.stackexchange.com/questions/6507/... gamedev.stackexchange.com/questions/19207/...
ทิมโฮลท์

คำตอบ:


17

ฉันใช้สิ่งที่คล้ายกับสิ่งนี้ ผมเขียนขึ้นโพสต์เกี่ยวกับเรื่องนี้ในบล็อกของฉัน: byte56.com/2011/06/a-light-post แต่ฉันจะเข้าไปดูรายละเอียดเพิ่มเติมที่นี่

ในขณะที่บทความ codeflow เชื่อมโยงในคำตอบอื่นที่น่าสนใจสวย จากสิ่งที่ฉันเข้าใจนั่นไม่ใช่วิธีที่ Minecraft ให้แสงสว่าง แสง Minecraft เป็นออโตมาตะแบบเซลลูลาร์มากกว่าแหล่งกำเนิดแสงแบบดั้งเดิม

ฉันถือว่าคุณคุ้นเคยกับการไหลของน้ำใน MC การให้แสงสว่างใน MC เป็นสิ่งเดียวกัน ฉันจะแนะนำคุณผ่านตัวอย่างง่ายๆ

ต่อไปนี้เป็นสิ่งที่คุณควรคำนึงถึง

  • เราจะเก็บรายการลูกบาศก์ที่ต้องตรวจสอบค่าแสง
  • เฉพาะลูกบาศก์โปร่งใสและก้อนเปล่งแสงเท่านั้นที่มีค่าแสง

ก้อนแรกที่เราเพิ่มคือแหล่งกำเนิดแสง แหล่งที่มาเป็นกรณีพิเศษ ค่าแสงถูกตั้งค่าตามประเภทของแหล่งกำเนิดแสง (ตัวอย่างเช่นคบเพลิงจะได้ค่าที่สว่างกว่าลาวา) หากคิวบ์มีการตั้งค่าแสงเหนือ 0 เราจะเพิ่มคิวบ์โปร่งใสทั้งหมดที่อยู่ติดกับคิวบ์นั้นลงในรายการ สำหรับแต่ละคิวบ์ในรายการเราตั้งค่าแสงเป็นเพื่อนบ้านที่สว่างที่สุดลบหนึ่ง ซึ่งหมายความว่าลูกบาศก์โปร่งใสทั้งหมด (รวมถึง "อากาศ") ถัดจากแหล่งกำเนิดแสงจะได้รับค่าแสง 15 เรายังคงเดินรอบก้อนรอบแหล่งกำเนิดแสงต่อไปเพิ่มก้อนที่ต้องตรวจสอบและนำก้อนไฟออกจากรายการ ใช้เราไม่มีอีกต่อไปที่จะเพิ่ม นั่นหมายความว่าชุดค่าล่าสุดทั้งหมดได้รับการตั้งค่าเป็น 0 ซึ่งหมายความว่าเราถึงจุดสิ้นสุดของแสงแล้ว

นั่นเป็นคำอธิบายที่ค่อนข้างง่ายของแสง ฉันได้ทำบางสิ่งที่ก้าวหน้าไปเล็กน้อย แต่ฉันเริ่มต้นด้วยหลักการพื้นฐานเดียวกัน นี่คือตัวอย่างของสิ่งที่ผลิต:

ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้คุณมีชุดข้อมูลแสงทั้งหมดแล้ว เมื่อคุณสร้างค่าสีสำหรับจุดยอดของคุณคุณสามารถอ้างอิงข้อมูลความสว่างนี้ คุณสามารถทำสิ่งนี้ได้ (โดยที่แสงสว่างคือค่า int ระหว่าง 0 ถึง 15):

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

โดยทั่วไปฉันจะใช้ค่าแสงตั้งแต่ 0 ถึง 1 ถึงกำลัง 1.4f สิ่งนี้ทำให้ฉันมืดดำเล็กน้อยกว่าฟังก์ชั่นเชิงเส้น ตรวจสอบให้แน่ใจว่าค่าสีของคุณไม่ไปเกิน 1 ฉันทำอย่างนั้นโดยหารด้วย 16 แทนที่จะเป็น 15 ดังนั้นฉันจึงมีห้องพิเศษเล็ก ๆ น้อย ๆ อยู่เสมอ จากนั้นย้ายที่พิเศษไปยังฐานดังนั้นฉันมักจะมีพื้นผิวเล็ก ๆ น้อย ๆ และไม่ได้เป็นสีดำบริสุทธิ์

จากนั้นใน shader ของฉัน (คล้ายกับไฟล์เอฟเฟกต์) ฉันจะได้รับสีแฟรกเมนต์สำหรับพื้นผิวและคูณด้วยสีแสงที่ฉันสร้างขึ้นด้านบน ซึ่งหมายความว่าความสว่างเต็มจะให้พื้นผิวตามที่สร้างขึ้น ความสว่างที่ต่ำมากทำให้พื้นผิวมีสีเข้มมาก (แต่ไม่ใช่สีดำเพราะสีพื้นฐาน)

แก้ไข

ในการรับแสงสำหรับใบหน้าคุณจะต้องมองลูกบาศก์ในทิศทางของใบหน้า ตัวอย่างเช่นใบหน้าด้านบนของคิวบ์ได้รับข้อมูลแสงจากคิวบ์ด้านบน

แก้ไข 2

ฉันจะพยายามตอบคำถามของคุณ

ดังนั้นสิ่งที่ฉันจะทำคือการเรียกซ้ำในกรณีนี้

คุณสามารถใช้อัลกอริทึมแบบเรียกซ้ำหรือวนซ้ำได้ มันขึ้นอยู่กับคุณว่าคุณต้องการใช้มันอย่างไร เพียงให้แน่ใจว่าคุณกำลังติดตามว่าก้อนใดถูกเพิ่มไปแล้วมิฉะนั้นคุณจะดำเนินต่อไปตลอดกาล

อัลกอริทึมจะ "ลดแสงลง" อย่างไร?

หากคุณกำลังพูดถึงแสงแดดแสงแดดจะแตกต่างกันเล็กน้อยเนื่องจากเราไม่ต้องการให้ความสว่างลดลง ข้อมูลคิวบ์ของฉันมี SKY บิต หากมีการทำเครื่องหมายก้อนเป็น SKY นั่นหมายความว่ามีการเข้าถึงที่ชัดเจนไปยังท้องฟ้าเปิดด้านบน SKY คิวบ์รับแสงเต็มรูปแบบลบระดับความมืดเสมอ จากนั้นก้อนถัดจากท้องฟ้าที่ไม่ใช่ท้องฟ้าเช่นทางเข้าถ้ำหรือที่แขวนยื่นขั้นตอนการให้แสงตามปกติใช้เวลามากกว่า หากคุณกำลังพูดถึงจุดแสงที่ส่องลงมา ... มันก็เหมือนกับทิศทางอื่น ๆ

ฉันจะระบุแสงสำหรับหน้าเดียวเท่านั้นได้อย่างไร

คุณไม่ได้ระบุแสงสำหรับหน้าเดียวเท่านั้น คิวบ์โปร่งใสแต่ละอันจะระบุแสงสำหรับคิวบ์แข็งทั้งหมดที่ใช้ใบหน้าร่วมกัน หากคุณต้องการแสงสำหรับใบหน้าเพียงแค่ตรวจสอบค่าแสงสำหรับก้อนโปร่งใสที่มันสัมผัส หากไม่ได้สัมผัสกับคิวบ์โปร่งใสคุณจะไม่สามารถแสดงผลได้

ตัวอย่างรหัส?

Nope


@ Byte56 ชอบที่จะรู้วิธีการแปลอัลกอริทึมนี้เพื่อทำงานกับ "chunks"
gopgop

ฉันคิดว่าเพียงแค่อัปเดตแต่ละด้านของก้อนตามเพื่อนบ้านแล้วเพิ่มบล็อกที่เปลี่ยนแปลงทั้งหมดลงในรายการบล็อกที่จะเปลี่ยน แต่ดูเหมือนจะไม่ทำงาน
gopgop

@gopgop ความคิดเห็นไม่ใช่สถานที่สำหรับการสนทนา คุณสามารถหาฉันในการแชทบางครั้ง หรือพูดคุยกับคนอื่นที่นั่น
MichaelHouse

4

อ่านบทความต่อไปนี้เนื่องจากควรให้ข้อมูลมากมายกับสิ่งที่คุณค้นหา มีหลายส่วนเกี่ยวกับแสง แต่โดยเฉพาะอย่างยิ่งอ่านหัวข้อเกี่ยวกับ Ambient Occlusion Light Observer Light และ Light Gathering

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

แต่พยายามตอบคำถามหลักของคุณ:

  • หากคุณต้องการทำให้สีเข้มขึ้นให้คูณด้วยสีอื่น (หรือเพียงแค่ลอยระหว่าง 0 และ 1 หากคุณต้องการทำให้ส่วนประกอบทั้งหมดมืดลงเท่ากัน) โดยที่ 1 ในองค์ประกอบย่อมาจากความเข้มเต็มในขณะที่ 0 หมายถึงความมืดเต็ม
  • หากคุณต้องการทำให้สีจางลงให้เพิ่มอีกสีหนึ่งแล้วจับที่ผลลัพธ์ แต่ระวังอย่าเลือกค่าที่สูงมากจนทำให้ผลอิ่มตัวไปถึงสีขาวบริสุทธิ์ เลือกสีเข้มพร้อมคำใบ้ที่คุณกำลังมองหาเช่นส้มเข้ม (# 886600)

    หรือ...

    หลังจากเพิ่มสีแทนที่จะยึดผลลัพธ์ (เช่นหนีบทุกองค์ประกอบของสีระหว่าง 0 และ 1) ปรับขนาดผลลัพธ์แทน - ค้นหาว่าองค์ประกอบใดในสามองค์ประกอบ (RGB) ที่ใหญ่ที่สุดและหารทั้งหมดด้วยค่านั้น วิธีนี้จะรักษาคุณสมบัติดั้งเดิมของสีได้ดีขึ้นเล็กน้อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.