समानांतर अनुमानित voxel इलाके जनरेटर


20

आपका काम एक ऊँचाई उत्पन्न करना है और इसे एक समानांतर अनुमानित, voxel परिदृश्य के रूप में दिखाना है। नियम निम्नलिखित हैं:

  • परिदृश्य की (ऊँचाई का) बेतरतीब ढंग से उत्पन्न किया जाना है
  • आपको यह भी वर्णन करना है कि एल्गोरिथ्म आप किस तरह से काम कर रहे हैं, इसलिए हर कोई यहां कुछ नया सीख सकता है
  • आपको या तो एक छवि उत्पन्न करनी होगी या स्क्रीन पर उत्पन्न परिदृश्य को प्रदर्शित करना होगा
  • परिणामी छवि को पैरेलल प्रोजेक्ट किया जाना है (इसलिए परिप्रेक्ष्य नहीं), और इसमें केवल वोक्सल्स हो सकते हैं (इसलिए इसे छोटे बक्से से बनाना होगा)
  • यह एक लोकप्रियता प्रतियोगिता है, इसलिए आप अपने प्रोग्राम में कुछ अतिरिक्त सुविधाएँ जोड़ना चाह सकते हैं ताकि अधिक से अधिक उत्थान मिल सके।
  • अंतिम वैध जमा करने के 7 दिन बाद विजेता सबसे अधिक मान्य मान्य उत्तर है। सभी मान्य सबमिशन को उपयोग किए गए एल्गोरिदम के विवरण सहित नियमों का पालन करने की आवश्यकता होती है। आप अतिरिक्त सुविधाओं को जोड़ सकते हैं जो कुछ नियमों का पालन नहीं करते हैं (जैसे कि परिप्रेक्ष्य मोड जोड़ना), लेकिन इस मामले में उन्हें वैकल्पिक विशेषताएं होनी चाहिए (उदाहरण के लिए जब उन्हें बंद करना है तो सभी नियमों का पालन करना चाहिए)
  • मेरा सबमिशन मान्य नहीं है।

एक उदाहरण परिणाम छवि निम्न है:

स्वर का परिदृश्य

यहाँ से ली गई छवि

यदि आपको कुछ एल्गोरिदम की आवश्यकता है तो यहां देखें


Minecraft esque प्रदान किए गए क्यूब्स समान स्वर नहीं हैं। यह भी सही आइसोमेट्रिक प्रक्षेपण की आवश्यकता है, या शब्द का उपयोग शिथिल है जैसा कि खेल में सामान्य है en.wikipedia.org/wiki/Video_games_with_isometric_graphics
shiona

@ सियोना: पार्सल को अनुमानित कहने के लिए कुछ दिनों पहले विषय का विवरण बदल दिया गया था, इसलिए कुछ भी गैर-परिप्रेक्ष्य को गिनना चाहिए। जैसा कि स्वरों के लिए: मुझे लगता है कि minecraftesqe क्यूब्स वोक्सल्स होने के संदर्भ में मान्य हैं: उन्हें एक बड़े 3 डी ग्रिड पर विशाल पिक्सेल माना जा सकता है।
२४:२५ को सस्टुप्य

नहीं, Minecraftesque क्यूब्स voxels नहीं हैं, क्योंकि voxels क्यूब्स नहीं हैं, ठीक उसी तरह जैसे कि पिक्सल स्क्वायर नहीं हैं। citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.79.9093
छद्म नाम

मैं @ छद्म नाम से सहमत हूं। मुझे लगता है कि यह वैध है हालांकि अगर आप चाहते हैं कि वे क्यूब्स बनें। यह हालांकि बहुत हर दूसरे voxel rasterization तकनीक को खत्म करता है।
टिम सेगुईन

जवाबों:


12

पायथन 2 3 डी फंक्शन प्लॉटर वोक्सल एडिशन

इस प्रतियोगिता में मेरी प्रविष्टि है:

import math
import random
import Image

terrain=""
randA=(random.random()*4+5)
randB=(random.random()*4+10)
randC=(random.random()*4+1)
randD=(random.random()*4+1)
im = Image.new("RGBA", (1248, 1000), "black")
tile=[Image.open("voxel/1.png"),Image.open("voxel/2.png"),Image.open("voxel/3.png"),Image.open("voxel/4.png"),Image.open("voxel/5.png"),Image.open("voxel/6.png"),Image.open("voxel/7.png"),Image.open("voxel/8.png"),Image.open("voxel/9.png")]


for y in range (-40,40):
        for x in range (-10, 10):
                val=int(1+abs(4+2.5*(math.sin(1/randA*x+randC)+math.sin(1/randB*y+randD))))
                if (val<9):
                        terrain+=str(val)
                else:
                        terrain+="9"
print terrain

for i in range (0,80*20):
        if((i/20)%2==0):
                shift=0
        else:
                shift=-32
        im.paste(tile[int(terrain[i])-1],((i%20)*64+shift,((i/20)*16-(32*(int(terrain[i])-1)))-32),tile[int(terrain[i])-1])

im.show()

जैसा कि शीर्षक में स्पष्ट रूप से कहा गया है कि यह एक 3 डी फ़ंक्शन प्लॉटर के रूप में काम करता है, लेकिन चूंकि इस प्रतियोगिता में इलाके को यादृच्छिक रूप से उत्पन्न करने की आवश्यकता है, इसलिए यह यादृच्छिक साइन फ़ंक्शन है 1.5*(math.sin(1/randA*x+randC)+math.sin(1/randB*y+randD))जो 4 यादृच्छिक चर पर निर्भर करता है। यह इस तरह से इलाके बनाता है: यादृच्छिक उत्पादन

हम इस यादृच्छिक फ़ंक्शन को किसी भी 2 चर फ़ंक्शन द्वारा बदल सकते हैं, उदाहरण के लिए sin(sqrt((x/2)²+(y/2)²))*3यह इलाक़ा देता है: 3Dfunction

और -x*y*e^(-x^2-y^2)यह देता है: 3Dfunction2
(दाईं ओर वाले भूखंडों की गणना वुल्फराम अल्फा द्वारा की जाती है)

और जब तक हम उस पर रहे हैं, महत्वपूर्ण पट्टी के साथ रीमैन ज़ेटा:

रीमैन ज़ेटा फंक्शन

उन लोगों के लिए जो इससे परिचित नहीं हैं, जैसा कि आप पानी के इन पूलों को देख सकते हैं (जो कार्य के शून्य का प्रतिनिधित्व करते हैं) सभी एक सीधी रेखा (वास्तविक भाग = 0.5) पर झूठ बोलते हैं। यदि आप यह साबित कर सकते हैं, तो आपको $ 1000000 मिलेंगे! इस लिंक को देखें।

मुझे आशा है कि आप इसे पसंद करते हो!


हाय जेन्स, अच्छे प्लॉट्स! मैं सोच रहा था कि आपको वोक्सिल तस्वीरें कहाँ से मिलीं?
विलेम

मुझे ठीक से याद नहीं है कि मैंने कहां, चित्रों के साथ एक gogle चित्र खोजा और संपादित किया
Jens Renders

10

सी #, डब्ल्यूपीएफ

मैंने एक यादृच्छिक चलने के साथ प्रयोग किया है , जो कि मेरे द्वारा पूर्वानुमानित की तुलना में बेहतर काम करता है। मैं नक्शे पर कहीं शुरू करता हूं, एक यादृच्छिक आसन्न टाइल पर चलता हूं और इसकी ऊंचाई मान बढ़ाता हूं , फिर अगले और इतने पर आगे बढ़ता हूं । यह हजारों बार दोहराया जाता है और अंततः इस तरह की ऊंचाई के नक्शे की ओर जाता है (100 x 100):

आवर्धित ऊंचाई का नक्शा ऊंचाई का नक्शा

फिर, मैं नक्शे को "विवेकाधीन" करता हूं, दिए गए ऊंचाई स्तरों पर मूल्यों की संख्या को कम करता हूं और उस प्रकार के आधार पर इलाके / रंग असाइन करता हूं:

बढ़ाई इलाके का नक्शा इलाके का नक्शा

voxel terrain १

अधिक समान द्वीपसमूह जैसे इलाके:

स्वर का भूभाग २

स्वर का भूभाग ३

voxel भूभाग ४

voxel भूभाग ५

अधिक पहाड़ी इलाकों को प्राप्त करने के लिए यादृच्छिक चरणों और ऊंचाई के स्तर में वृद्धि:

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें


कोड

विशेषताएं: एक बटन के साथ इलाके विश्राम। 3 डी इलाके और 2 डी मानचित्र दिखाएं। ज़ूमिंग (माउस व्हील) और 3 डी स्क्रॉल (तीर कुंजी)। लेकिन यह बहुत अच्छा नहीं है - आखिरकार, यह विशुद्ध रूप से WPF में लिखा गया है, DirectX या OpenGL में नहीं।

MainWindow.xaml:

<Window x:Class="VoxelTerrainGenerator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Voxel Terrain Generator" Width="550" Height="280" KeyUp="Window_KeyUp">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Viewport3D x:Name="ViewPort" MouseWheel="ViewPort_MouseWheel">
            <Viewport3D.Camera>
                <OrthographicCamera x:Name="Camera" Position="-100,-100,150" LookDirection="1,1,-1" UpDirection="0,0,1" Width="150" />
                <!--<PerspectiveCamera x:Name="Camera" Position="-100,-100,150" LookDirection="1,1,-1" UpDirection="0,0,1" />-->
            </Viewport3D.Camera>
        </Viewport3D>

        <Grid Grid.Column="1" Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <Image Grid.Row="0" x:Name="TopViewImage"/>
            <Button Grid.Row="1" Margin="0 10 0 0" Click="Button_Click" Content="Generate Terrain" />
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Media3D;

namespace VoxelTerrainGenerator
{
    public partial class MainWindow : Window
    {
        const int RandomSteps = 20000;
        const int MapLengthX = 100;
        const int MapLengthY = 100;
        const int MaxX = MapLengthX - 1;
        const int MaxY = MapLengthY - 1;
        const bool ForceIntoBounds = true;
        readonly Random Random = new Random();

        readonly List<Color> ColorsByHeight = new List<Color> 
        { 
            Color.FromArgb(0, 0, 50),
            Color.FromArgb(170, 170, 20),
            Color.FromArgb(0, 150, 0),
            Color.FromArgb(0, 140, 0),
            Color.FromArgb(0, 130, 0),
            Color.FromArgb(0, 120, 0),
            Color.FromArgb(0, 110, 0),
            Color.FromArgb(100, 100, 100),
        };

        public MainWindow()
        {
            InitializeComponent();
            TopViewImage.Width = MapLengthX;
            TopViewImage.Height = MapLengthY;
        }

        public int[,] CreateRandomHeightMap()
        {
            var map = new int[MapLengthX, MapLengthY];

            int x = MapLengthX/2;
            int y = MapLengthY/2;

            for (int i = 0; i < RandomSteps; i++)
            {
                x += Random.Next(-1, 2);
                y += Random.Next(-1, 2);

                if (ForceIntoBounds)
                {
                    if (x < 0) x = 0;
                    if (x > MaxX) x = MaxX;
                    if (y < 0) y = 0;
                    if (y > MaxY) y = MaxY;
                }

                if (x >= 0 && x < MapLengthX && y >= 0 && y < MapLengthY)
                {
                    map[x, y]++;
                }
            }

            return map;
        }

        public int[,] Normalized(int[,] map, int newMax)
        {
            int max = map.Cast<int>().Max();
            float f = (float)newMax / (float)max;

            int[,] newMap = new int[MapLengthX, MapLengthY];
            for (int x = 0; x < MapLengthX; x++)
            {
                for (int y = 0; y < MapLengthY; y++)
                {
                    newMap[x, y] = (int)(map[x, y] * f);
                }
            }
            return newMap;
        }

        public Bitmap ToBitmap(int[,] map)
        {
            var bitmap = new Bitmap(MapLengthX, MapLengthY);
            for (int x = 0; x < MapLengthX; x++)
            {
                for (int y = 0; y < MapLengthY; y++)
                {
                    int height = map[x, y];
                    if (height > 255)
                    {
                        height = 255;
                    }
                    var color = Color.FromArgb(255, height, height, height);
                    bitmap.SetPixel(x, y, color);
                }
            }
            return bitmap;
        }

        public Bitmap ToColorcodedBitmap(int[,] map)
        {
            int maxHeight = ColorsByHeight.Count-1;
            var bitmap = new Bitmap(MapLengthX, MapLengthY);
            for (int x = 0; x < MapLengthX; x++)
            {
                for (int y = 0; y < MapLengthY; y++)
                {
                    int height = map[x, y];
                    if (height > maxHeight)
                    {
                        height = maxHeight;
                    }
                    bitmap.SetPixel(x, y, ColorsByHeight[height]);
                }
            }
            return bitmap;
        }

        private void ShowTopView(int[,] map)
        {
            using (var memory = new System.IO.MemoryStream())
            {
                ToColorcodedBitmap(map).Save(memory, ImageFormat.Png);
                memory.Position = 0;
                var bitmapImage = new System.Windows.Media.Imaging.BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = memory;
                bitmapImage.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                TopViewImage.Source = bitmapImage;
            }
        }

        private void Show3DView(int[,] map)
        {
            ViewPort.Children.Clear();

            var light1 = new AmbientLight(System.Windows.Media.Color.FromArgb(255, 75, 75, 75));
            var lightElement1 = new ModelUIElement3D();
            lightElement1.Model = light1;
            ViewPort.Children.Add(lightElement1);

            var light2 = new DirectionalLight(
                System.Windows.Media.Color.FromArgb(255, 200, 200, 200),
                new Vector3D(0, 1, -0.1));
            var lightElement2 = new ModelUIElement3D();
            lightElement2.Model = light2;
            ViewPort.Children.Add(lightElement2);

            for (int x = 0; x < MapLengthX; x++)
            {
                for (int y = 0; y < MapLengthY; y++)
                {
                    int height = map[x, MapLengthY-y-1];
                    for (int h = 0; h <= height; h++)
                    {
                        Color color = ColorsByHeight[h];
                        if (height > 0 && h == 0)
                        {
                            // No water under sand
                            color = ColorsByHeight[1];
                        }

                        ViewPort.Children.Add(CreateCube(x, y, h, 1,
                            System.Windows.Media.Color.FromArgb(255, color.R, color.G, color.B)));
                    }
                }
            }
        }

        private ModelVisual3D CreateCube(int x, int y, int z, int length,
            System.Windows.Media.Color color)
        {
            List<Point3D> positions = new List<Point3D>()
            {
                new Point3D(x, y, z),
                new Point3D(x + length, y, z),
                new Point3D(x + length, y + length, z),
                new Point3D(x, y + length, z),
                new Point3D(x, y, z + length),
                new Point3D(x + length, y, z + length),
                new Point3D(x + length, y + length, z + length),
                new Point3D(x, y + length, z + length),
            };

            List<List<int>> quads = new List<List<int>> 
            { 
                new List<int> {3,2,1,0},
                new List<int> {0,1,5,4},
                new List<int> {2,6,5,1},
                new List<int> {3,7,6,2},
                new List<int> {3,0,4,7},
                new List<int> {4,5,6,7},
            };

            double halfLength = (double)length / 2.0;
            Point3D cubeCenter = new Point3D(x + halfLength, y + halfLength, z + halfLength);
            var mesh = new MeshGeometry3D();
            foreach (List<int> quad in quads)
            {
                int indexOffset = mesh.Positions.Count;
                mesh.Positions.Add(positions[quad[0]]);
                mesh.Positions.Add(positions[quad[1]]);
                mesh.Positions.Add(positions[quad[2]]);
                mesh.Positions.Add(positions[quad[3]]);

                mesh.TriangleIndices.Add(indexOffset);
                mesh.TriangleIndices.Add(indexOffset+1);
                mesh.TriangleIndices.Add(indexOffset+2);
                mesh.TriangleIndices.Add(indexOffset+2);
                mesh.TriangleIndices.Add(indexOffset+3);
                mesh.TriangleIndices.Add(indexOffset);

                double centroidX = quad.Select(v => mesh.Positions[v].X).Sum() / 4.0;
                double centroidY = quad.Select(v => mesh.Positions[v].Y).Sum() / 4.0;
                double centroidZ = quad.Select(v => mesh.Positions[v].Z).Sum() / 4.0;
                Vector3D normal = new Vector3D(
                    centroidX - cubeCenter.X,
                    centroidY - cubeCenter.Y,
                    centroidZ - cubeCenter.Z);
                for (int i = 0; i < 4; i++)
                {
                    mesh.Normals.Add(normal);
                }
            }

            Material material = new DiffuseMaterial(new System.Windows.Media.SolidColorBrush(color));
            GeometryModel3D model = new GeometryModel3D(mesh, material);
            ModelVisual3D visual = new ModelVisual3D();
            visual.Content = model;
            return visual;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            int[,] map = CreateRandomHeightMap();
            int[,] normalizedMap = (Normalized(map, ColorsByHeight.Count-1));

            ShowTopView(normalizedMap);
            Show3DView(normalizedMap);

            ToBitmap(Normalized(map, 255)).Save("heightmap-original.png");
            ToBitmap(Normalized(normalizedMap, 255)).Save("heightmap.png");
            ToColorcodedBitmap(normalizedMap).Save("terrainmap.png");
        }

        private void ViewPort_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            // Zoom in or out
            Camera.Width -= (double)e.Delta / 100;
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            // Scrolling by moving the 3D camera
            double x = 0;
            double y = 0;
            if (e.Key == Key.Left)
            {
                x = +10;
                y = -10;
            }
            else if (e.Key == Key.Up)
            {
                x = -10;
                y = -10;
            }
            else if (e.Key == Key.Right)
            {
                x = -10;
                y = +10;
            }
            else if (e.Key == Key.Down)
            {
                x = +10;
                y = +10;
            }

            Point3D cameraPosition = new Point3D(
                Camera.Position.X + x,
                Camera.Position.Y + y,
                Camera.Position.Z);
            Camera.Position = cameraPosition;
        }
    }
}

नीट, लेकिन यह बेहतर हो सकता है जब आप अधिक बाल्टी को शामिल करने के लिए 'विवेक' करते हैं। शायद केवल एक या दो और समूह? (किसी भी तरह से आवश्यक नहीं! फिर भी मेरे से +1।)
गफ्फी

1
@ गफ्फी मैंने और भी परिणाम जोड़ दिए हैं, जिसमें और भी पहाड़ी लोग शामिल हैं
सेबेस्टियन नेग्रैज़स

4

जावास्क्रिप्ट और Crafty.JS, बहुत सुधार किया जाना है

यहाँ एक नमूना आउटपुट है:

स्क्रीनशॉट

और यहाँ कोड (पूर्ण वेबपेज) है:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
    <script type="text/javascript" src="http://craftyjs.com/release/0.4.2/crafty-min.js"></script>
    <script type="text/javascript">

    $(document).ready(function() {
        Crafty.init();

        var tilesize = 20
        Crafty.sprite(20, "sprite.png#UPDATE", {
            grass1: [0,0,1,3],
            grass2: [1,0,1,3],
            grass3: [2,0,1,3],
            stone1: [3,0,1,3],
            stone2: [4,0,1,3]
        });

        genTerrainInit()
        while(1) {
            try { stepTerrainGen() }
            catch(e) { break }
        }


        iso = Crafty.isometric.init(20);
        var z = 0;
        for(var i = tdata.length - 1; i >= 0; i--) {
            for(var y = 0; y < tdata[i].length; y++) {
                var which = Math.max(0, Math.round(tdata[i][y]))
                var tile = Crafty.e("2D, DOM, "+["grass1", "grass2", "grass3", "stone1", "stone2"][which])
                .attr('z',i+1 * y+1)

                iso.place(i,y,0, tile);
            }
        }

        Crafty.addEvent(this, Crafty.stage.elem, "mousedown", function(e) {
            if(e.button > 1) return;
            var base = {x: e.clientX, y: e.clientY};

            function scroll(e) {
                var dx = base.x - e.clientX,
                    dy = base.y - e.clientY;
                    base = {x: e.clientX, y: e.clientY};
                Crafty.viewport.x -= dx;
                Crafty.viewport.y -= dy;
            };

            Crafty.addEvent(this, Crafty.stage.elem, "mousemove", scroll);
            Crafty.addEvent(this, Crafty.stage.elem, "mouseup", function() {
                Crafty.removeEvent(this, Crafty.stage.elem, "mousemove", scroll);
            });
        });
    });

    function genTerrainInit() {
        //Variables
        size = Math.pow(2, 6) + 1; //MUST be a power of 2 plus 1!
        initHeight = 2;
        rndRange = 4;
        smoothSpeed = 0.5; // lower is faster

        tdata = new Array(size);
        toAverage = new Array(size);
        for (var i = 0; i < size; i ++) {
            tdata[i] = new Array(size);
            toAverage[i] = new Array(size);
            for (var i2 = 0; i2 < size; i2 ++) {
                tdata[i][i2] = null;
                toAverage[i][i2] = false;
            }
        }

        //Generate corners
        tdata[0][0] = initHeight;
        tdata[size-1][0] = initHeight;
        tdata[0][size-1] = initHeight;
        tdata[size-1][size-1] = initHeight;
    }

    function stepTerrainGen() {
        //The square step - for each square, take the center point and set it to the average of its corners plus a random amount
        oldi = 0;
        for (var i = 1; i < size; i ++) {
            if (tdata[0][i] != null) {
                oldi2 = 0;
                for (var i2 = 1; i2 < size; i2 ++) {
                    if (tdata[i2][i] != null) {
                        pointDistance = (i - oldi)/2;
                        tdata[(oldi2 + i2)/2][(oldi + i)/2] =
                            ((tdata[oldi2][oldi] + tdata[i2][oldi] + tdata[oldi2][i] + tdata[i2][i])/4) // average of 4 corners
                            + Math.random() * rndRange - (rndRange/2.0);                                // plus a random amount

                        // Now mark the squares for the diamond step
                        toAverage[(oldi2 + i2)/2][oldi] = true;
                        toAverage[oldi2][(oldi + i)/2] = true;
                        toAverage[(oldi2 + i2)/2][i] = true;
                        toAverage[i2][(oldi + i)/2] = true;
                        oldi2 = i2;
                    }
                }
                oldi = i;
            }
        }

        //The diamond step - same as the square step but with newly formed diamonds
        for (var i = 0; i < size; i ++) {
            for (var i2 = 0; i2 < size; i2 ++) {
                if (toAverage[i][i2]) {
                    diamondArray = [];
                    if (i-pointDistance >= 0) diamondArray = diamondArray.concat(tdata[i-pointDistance][i2]);
                    if (i+pointDistance < size) diamondArray = diamondArray.concat(tdata[i+pointDistance][i2]);
                    if (i2-pointDistance >= 0) diamondArray = diamondArray.concat(tdata[i][i2-pointDistance]);
                    if (i2+pointDistance < size) diamondArray = diamondArray.concat(tdata[i][i2+pointDistance]);
                    addedPoints = 0;
                    for (var i3 = 0; i3 < diamondArray.length; i3 ++) addedPoints += diamondArray[i3];
                    tdata[i][i2] = addedPoints/diamondArray.length + Math.floor(Math.random() * rndRange - (rndRange/2.0));
                }
            }
        }
        rndRange *= smoothSpeed;
        resetToAverage();
    }

    function resetToAverage() {
        for (var i = 0; i < size; i ++) {
            for (var i2 = 0; i2 < size; i2 ++) {
                toAverage[i][i2] = false;
            }
        }
    }

    </script>
    <title>Iso</title>
    <style>
    body, html { margin:0; padding: 0; overflow:hidden }
    </style>
</head>
<body>
</body>
</html>

यहाँ है sprite.png:

sprite.png

अब, मेरे पास कहने के लिए कुछ चीजें हैं।

  1. मुझे उस भयानक कोड के लिए न्याय मत करो! : PI ने इसे कई साल पहले लिखा था जब मैं एक भयानक प्रोग्रामिंग था। वास्तव में, यह उस वेबसाइट के पुराने दिनों से है जो मेरे पास था कि मुझे यह भी याद नहीं था कि मेरे पास है! http://oddllama.cu.cc/terrain/

  2. मैं Crafty.JS Isometric डेमो से कोड की नकल की तरह की तरह : पी

  3. स्पष्टीकरण जल्द ही आ जाएगा! मुझे अब सोने जाना है क्योंकि यहाँ देर हो चुकी है। (यही कारण है कि स्प्राइट इतना भयानक है!)

असल में, यह वास्तव में अनप्लिश है और बाद में इसमें बहुत सुधार होगा!

यह ओपी के उत्तर में उल्लिखित हीरे-वर्ग एल्गोरिथ्म का उपयोग करता है।


क्या हम अन्य भाषाओं में उपयोग के लिए उन स्प्राइट्स को उधार ले सकते हैं?
PyRulez

@PyRulez ठीक है, मैं, उह, एक तरह से उन्हें चुरा लिया (और उन्हें संपादित), Crafty.JS साइट से तो मैं पता नहीं है: पी शायद मैं उल्लेख किया जाना चाहिए था कि
दरवाज़े

3

रूबी + RMagick

मैं ऊँचाई उत्पन्न करने के लिए डायमंड-स्क्वायर एल्गोरिथ्म का उपयोग कर रहा हूँ ।

संक्षेप में एल्गोरिथ्म:

  • 2 ^ n के आकार के साथ एक रैपिंग ऐरे मैट्रिक्स का उपयोग करें
  • रैपिंग का मतलब है कि सीमा के बाहर कोई भी सूचकांक चारों ओर से लपेटता है, जैसे कि सरणी का आकार 4 है [0,0] == [4,0] == [0,4] == [4,4]। इसके अलावा [-2,0] == [2,0], आदि।
  • [0,0]एक यादृच्छिक रंग पर सेट करें
  • छवि में दिखाए गए चरणों का पालन करें।

स्पष्टीकरण छवि

  • ध्यान दें, कि जब सरणी चारों ओर घूमती है, जब आपको सीमा के बाहर कुछ इंडेक्स करना होता है, तो आप सरणी के दूसरी तरफ से डेटा का उपयोग कर सकते हैं।
  • यह भी ध्यान दें, कि पहले ही चरण में चार कोनों का अर्थ बिल्कुल वैसा ही है जैसा (मान [0,0] == [4,0] == [0,4] == [4,4])
  • ब्लैक डॉट के मूल्य की गणना करने के लिए, आपको उसके चारों ओर के चार बिंदुओं को औसत करना होगा
  • जैसा कि यह एक उबाऊ, ग्रे छवि के परिणामस्वरूप होगा, आपको प्रत्येक चरण में इस मान में एक यादृच्छिक संख्या जोड़ना होगा। यह पसंद किया जाता है कि यह यादृच्छिक मान पहली श्रेणी में पूरी सीमा तक फैला हो, लेकिन समय के साथ कम हो जाता है जब आप सरणी के छोटे और छोटे सबसेट को संबोधित कर रहे होते हैं। समय के साथ यह यादृच्छिकता कम हो जाती है, छवि जितनी अधिक शोर होगी।

  • के बाद मैं कर रहा हूँ मैं बस एक ऊंचाई मूल्य के लिए एक रंग प्रदान कर रहा हूँ।

कोड:

generate.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'bundler/setup'
Bundler.require(:default)

class Numeric
  def clamp min, max
    [[self, max].min, min].max
  end
end

class WrappedArray
  def initialize(size)
    @size = size
    @points = Array.new(size){Array.new(SIZE)}
  end
  def [](y,x)
    @points[(@size+y) % @size][(@size+x) % @size]
  end
  def []=(y,x,value)
    @points[(@size+y) % @size][(@size+x) % @size] = value.clamp(0,@size*@size-1)
  end
end

SIZE = 256
MAXHEIGHT = 256*256

points = WrappedArray.new(SIZE)

points[0,0] = 0

s = SIZE
d = []
sq = []
r = MAXHEIGHT
while s>1
  (0...SIZE).step(s) do |x|
    (0...SIZE).step(s) do |y|
      d << [y,x]
    end
  end
  while !d.empty?
    y,x = *d.shift
    mx = x+s/2
    my = y+s/2

    points[my,mx]  = (points[y,x]   + points[y,x+s]      + points[y+s,x] + points[y+s,x+s])/4 + rand(r)-r/2
    sq << [my,x]
    sq << [my,x+s]
    sq << [y,mx]
    sq << [y+s,mx]
  end
  while !sq.empty?
    y,x = *sq.shift
    points[y,x]    = (points[y-s/2,x] + points[y+s/2,x] + points[y,x-s/2] + points[y,x+s/2])/4 + rand(r)-r/2
  end
  s = s / 2
  r = r * 2 / 3
end

def get_color(height)
  val = height.to_f/MAXHEIGHT*3-1
  r = 0
  g = 0
  b = 0
  if val<=-0.25
    Magick::Pixel.new(0,0,128*256)
  elsif val<=0
    Magick::Pixel.new(0,0,255*256)
  elsif val<=0.0625
    Magick::Pixel.new(0,128*256,255*256)
  elsif val<=0.1250
    Magick::Pixel.new(240*256,240*256,64*256)
  elsif val<=0.3750
    Magick::Pixel.new(32*256,160*256,0)
  elsif val<=0.7500
    Magick::Pixel.new(224*256,224*256,0)
  else
    Magick::Pixel.new(128*256,128*256,128*256)
  end
end

canvas = Magick::ImageList.new
canvas.new_image(SIZE+1, SIZE+1)
0.upto(SIZE) do |y|
  0.upto(SIZE) do |x|
    canvas.pixel_color(x,y,get_color(points[y,x]))
  end
end
canvas.write('result.png')

Gemfile

source "https://rubygems.org"
gem 'rmagick'

नोट: मैं जिस इमेजमैजिक का उपयोग कर रहा हूं वह 16 बिट है

परिणाम छवि:

परिणाम छवि

नोट: यह छवि एक टॉप-डाउन, आइसोमेट्रिक प्रतिनिधित्व है, जहाँ एक स्वर का आकार बिल्कुल एक पिक्सेल है, इसलिए यह नियमों के अनुसार मान्य है (एक को छोड़कर: मेरा उत्तर मान्य नहीं माना गया है)


क्या आपके एक-पिक्सेल टॉप-डाउन आइसोमेट्रिक सॉल्यूशन की गुणवत्ता का मतलब उस गंभीरता के संकेतक के रूप में है जिसके साथ आप चाहते हैं कि लोग आपके सवाल का जवाब दें?
जोनाथन वान माट्रे

मुझे नहीं लगता कि आइसोमेट्रिक के रूप में टॉप डाउन मायने रखता है? en.wikipedia.org/wiki/Isometric_projection
matnewport

@JonathanVanMatre: प्रश्न में मैंने वांछित परिणाम दिखाया। उत्तर में मैंने न्यूनतम दिखाया कि आपको उत्तर मान्य होने के लिए क्या करना चाहिए। जैसा कि यह एक लोकप्रियता-प्रतियोगिता है जिसे आप चुन सकते हैं कि आप क्या करना चाहते हैं, लेकिन निश्चित रूप से आपको वांछित परिणाम प्राप्त करने के लिए रोमांचित होना चाहिए।
SztupY

@mattnewport: good point, मैं गलती से सभी प्रकार के पैरेलल अनुमानों के लिए इसका उपयोग कर रहा था । फिक्स्ड।
SztupY

3

जावा (आधार एल्गोरिथ्म के रूप में @ fejesjoco की रंगीन छवि का उपयोग करके)

फुल आरजीबी रंग चित्रों के साथ थोड़ा सा खेलने के बाद @ फैजेसोको से मैंने देखा कि उन्हें दिलचस्प क्लिफी वॉक्सल परिदृश्य के लिए आधार के रूप में इस्तेमाल किया जा सकता है। एल्गोरिथ्म को फिर से लागू करने के बजाय मैंने उनके कोड को बाहरी निष्पादन योग्य के रूप में इस्तेमाल किया (इसे http://joco.name/2014/03/02/all-rgb-colors-in-one-image/ से डाउनलोड करें और इसे आर्टगन नाम दिया। उसी निर्देशिका में निर्वासित)

पूर्वावलोकन:
पूर्वावलोकन

ऊँचाई का उपयोग (नीले चैनल में संग्रहीत)
Heightmap

इनपुट छवि:
इनपुट

मेरे द्वारा उपयोग किया जाने वाला सब-अल्गोरिथम उस तरह से काम करता है:
1. छँटाई
2. केंद्र पर एक काले पिक्सेल से शुरू करें
। 3. जब तक सभी रंगों का उपयोग न हो जाए: वर्तमान रंग को निकटतम फिटिंग स्थान पर रखें और अप्रयुक्त पड़ोसियों को नए प्रयोग योग्य स्थानों के रूप में जोड़ें। जब यह समाप्त हो गया तो मैंने इसे घटाकर 256 अलग-अलग मूल्यों पर लाने के लिए red&(green|blue) 4. तब मैंने पहले से तैयार स्प्राइट का उपयोग किया और परत के साथ छवि परत उत्पन्न की

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.imageio.ImageIO;
import javax.xml.bind.DatatypeConverter;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author LH
 */
public class Voxelizer2
{
    static final String zipembeddedsprites =
            "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";
    public static void main(String[] args) throws Exception
    {
        //embedded zip idea borrowed from over here:
        //http://codegolf.stackexchange.com/a/22262/10801

        //algorithm and embedded executable borrowed from
        //http://joco.name/2014/03/02/all-rgb-colors-in-one-image/

        //256 8192 2048 4096 1024 1000 9263 11111111 hue-0 one
        /**/
        ProcessBuilder p = new ProcessBuilder("artgen","64","512","512","256","256","1",((int)(Math.random()*(2<<32)))+"","11111111","hue-0","one");
        Process po = p.start();
        BufferedReader x = new BufferedReader(new InputStreamReader(po.getInputStream()),1024);
        String xl = x.readLine();
        //String x2l = x2.readLine();
        while(!xl.startsWith("Press ENTER to exit"))
        {
            System.out.println(xl);
            xl=x.readLine();
        }
        System.out.println(xl);
        po.destroy();/**/
        BufferedImage source = ImageIO.read(new File("result00000.png"));
        BufferedImage heightmap = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < source.getWidth(); i++)
        {
            for (int j = 0; j < source.getHeight(); j++)
            {
                int basecolor=source.getRGB(i, j)&0x00FFFFFF;
                int r = (basecolor&0x00FF0000)>>16;
                int g = (basecolor&0x0000FF00)>>8;
                int b = (basecolor&0x000000FF);
                int color = r&(g|b);//Math.max(r,Math.max(g,b));
                heightmap.setRGB(i, j, color);

            }
        }/**/
        ImageIO.write(heightmap, "png", new File("heightmap.png"));


        //generate sizedata for Sprites....

        ZipInputStream zippedSprites = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(zipembeddedsprites)));
        ZipEntry z = zippedSprites.getNextEntry();
        BufferedImage water=null,grass=null,stone=null,air = new BufferedImage(24,24, BufferedImage.TYPE_INT_ARGB);
        while(z!=null)
        {
            String name = z.getName();
            switch(name)
            {
                case "water.png":
                    water=ImageIO.read(zippedSprites);
                    System.out.println("water");
                break;
                case "stone.png":
                    stone=ImageIO.read(zippedSprites);
                    System.out.println("stone");
                break;
                case "grass.png":
                    grass=ImageIO.read(zippedSprites);
                    System.out.println("grass");
                break;
            }
            z=zippedSprites.getNextEntry();
        }

        //int height = heightmap.getHeight()*12+12;
        int width16 = heightmap.getWidth()/16;
        int height16=heightmap.getHeight()/16;
        int widthtemp1 = 384+(height16-1)*(384/2);
        int width = (width16-1)*(384/2)+widthtemp1;
        //int heightt1=height16*(12*16)+12*16;
        int height = (width16-1)*(12*16)+(12*16);
        System.out.println(width*height);
        //if(true)return;

        int StartPos =heightmap.getHeight()*12;

        //BufferedImage[] layers = new BufferedImage[256];

        BufferedImage complete = new BufferedImage(width, height+(255*12), BufferedImage.TYPE_INT_ARGB);
        int mergeOffset=255*12;
        for (int i = 0; i < 256; i++)
        {
            System.out.println("Rendering layer"+i);
            BufferedImage layer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            int basePointerX = StartPos-12;
            int basePointerY=0;
            Graphics g = layer.getGraphics();
            for (int k = 0; k < heightmap.getHeight(); k++)
            {
                //System.out.println("Processing line"+k);
                int pointerX = basePointerX;
                int pointerY = basePointerY;
                for (int j = 0; j < heightmap.getWidth(); j++)
                {

                    Image tile = air;
                    int pxheight =heightmap.getRGB(j, k)&0x00FFFFFF;
                    if(pxheight>i)
                    {
                        tile=stone;
                    }
                    if(pxheight==i)
                    {
                        if(i<64)
                        {
                            tile=stone;
                        }
                        else
                        {
                            tile=grass;
                        }
                    }
                    if(pxheight<i)
                    {
                        if(i<64)
                        {
                            tile=water;
                        }
                        else
                        {
                            tile=air;
                        }
                    }
                    g.drawImage(tile, pointerX, pointerY, null);
                    pointerX+=12;
                    pointerY+=6;
                }

                basePointerX-=12;
                basePointerY+=6;


            }

            //

            complete.getGraphics().drawImage(layer, 0, mergeOffset, null);

            mergeOffset-=12;
        }
        ImageIO.write(complete, "png", new File("landscape.png"));
    }
}

1

HTML + जावास्क्रिप्ट

यहाँ प्रतियोगिता में मेरा प्रयास है:

<html>
    <head>
        <script type='text/javascript' language='JavaScript'>
            function create() {
                var con = document.getElementById("can").getContext("2d"),
                    map = new Array(),
                    p = new Array(15 + Math.floor(Math.random() * 10)),
                    tc = ["#000040", "#000070", "#0000a0", "#5050ff", "#f0f000", "#007000", "#00aa00", "#00c000", "#00e000", "#00ff00", "#90ff90", "#a0ffa0", "#c0ffc0", "#e0ffe0", "#f0fff0"],
                    sc = ["#000020", "#000050", "#000085", "#3030df", "#d0d000", "#005000", "#008000", "#008000", "#00b500", "#00d000", "#00ea00", "#80ff80", "#a0ffa0", "#c0ffc0", "#d0ffd0"];
                for (var n = 0; n < p.length; n++) {
                    p[n] = [15 + Math.floor(Math.random() * 70), 15 + Math.floor(Math.random() * 70)];
                }
                for (var x = 0; x < 100; x++) {
                    map[x] = new Array();
                    for (var y = 0; y < 100; y++) {
                        map[x][y] = 0;
                        for (var n = 0; n < p.length; n++) {
                            if (20 - Math.sqrt(Math.pow(x - p[n][0], 2) + Math.pow(y - p[n][1], 2)) > map[x][y]) {
                                map[x][y] = 20 - Math.sqrt(Math.pow(x - p[n][0], 2) + Math.pow(y - p[n][2], 2));
                            }
                        }
                    }
                }
                for (var x = 0; x < 100; x++) {
                    for (var y = 0; y < 100; y++) {
                        if (map[x][y] < 0) {
                            map[x][y] = 0;
                        }
                        map[x][y] = Math.floor(map[x][y] / 2);
                        con.fillStyle = tc[map[x][y]];
                        con.fillRect(x * 10, y * 10 - map[x][y] * 4, 10, 10);
                        con.fillStyle = sc[map[x][y]];
                        con.fillRect(x * 10, y * 10 - map[x][y] * 4 + 10, 10, map[x][y] * 4);
                    }
                }
            }
        </script>
    </head>
    <body>
        <canvas id='can' width='1000' height='1000' style='border: 1px solid #000000;'></canvas>
        <button onclick='create();'>Create</button>
    </body>
</html>

मैं एक ऊंचाई बनाने के लिए यूक्लिडियन एफ 1 सेल नॉयस एल्गोरिथ्म का उपयोग करता हूं, जो तब मैं एक सरणी से उपयुक्त रंग लेकर और 10x पर एक वर्ग खींचकर एक छवि में बदल देता हूं, 10y-height इतनी अधिक पिक्सेल ऊपर उठाए जाते हैं। फिर मैं एक अलग सरणी से एक ही रंग का उपयोग करते हुए एक आयत खींचता हूं।

सेल शोर 1 सेल शोर २

यहाँ एक ही कोड 10,000-स्टेप रैंडम वॉक एल्गोरिदम का उपयोग किया गया है:

<html>
    <head>
        <script type='text/javascript' language='JavaScript'>
            function create() {
                var con = document.getElementById("can").getContext("2d"),
                    map = new Array(),
                    l = 10000,
                    tc = ["#000040", "#000070", "#0000a0", "#5050ff", "#f0f000", "#007000", "#00aa00", "#00c000", "#00e000", "#00ff00", "#90ff90", "#a0ffa0", "#c0ffc0", "#e0ffe0", "#f0fff0"],
                    sc = ["#000020", "#000050", "#000085", "#3030df", "#d0d000", "#005000", "#008000", "#008000", "#00b500", "#00d000", "#00ea00", "#80ff80", "#a0ffa0", "#c0ffc0", "#d0ffd0"];
                for (var x = 0; x < 100; x++) {
                    map[x] = new Array();
                    for (var y = 0; y < 100; y++) {
                        map[x][y] = 0;
                    }
                }
                x = 49;
                y = 49;
                for (var n = 0; n < l; n++) {
                    var d = Math.floor(Math.random() * 4);
                    if (d == 0) {
                        x++
                    }
                    else if (d == 1) {
                        y++
                    }
                    else if (d == 2) {
                        x--
                    }
                    else if (d == 3) {
                        y--
                    }
                    map[(x % 100 + 100) % 100][(y % 100 + 100) % 100]++;
                }
                for (var x = 0; x < 100; x++) {
                    for (var y = 0; y < 100; y++) {
                        if (map[x][y] < 0) {
                            map[x][y] = 0;
                        }
                        map[x][y] = Math.floor(map[x][y] / 2);
                        con.fillStyle = tc[map[x][y]];
                        con.fillRect(x * 10, y * 10 - map[x][y] * 4, 10, 10);
                        con.fillStyle = sc[map[x][y]];
                        con.fillRect(x * 10, y * 10 - map[x][y] * 4 + 10, 10, map[x][y] * 4);
                    }
                }
            }
        </script>
    </head>
    <body>
        <canvas id='can' width='1000' height='1000' style='border: 1px solid #000000;'></canvas>
        <button onclick='create();'>Create</button>
    </body>
</html>

यादृच्छिक चलना 1 [[रैंडम वॉक २] [४]

जब यह एक किनारे से 'चलता है' तो यह दूसरे पर लपेटता है, इसलिए यह अभी भी अच्छा दिखता है।

यह अभी भी तकनीकी रूप से समानांतर है, बस एक अलग कोण से।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.