简体   繁体   English

自动在画布中生成像素行星

[英]Auto generate pixel planets in canvas

My goal is to generate a random "realistic" planet with JavaScript. 我的目标是使用JavaScript生成一个随机的“逼真”行星。 The problem i have is to make it look good and not just random colors. 我遇到的问题是让它看起来不错,而不仅仅是随机颜色。 My current script takes colors between 2 random generated colors and fills the sphere with a random color between the 2 values. 我当前的脚本采用2种随机生成的颜色之间的颜色,并在2个值之间用随机颜色填充球体。 My goal is to have something that looks close(er) to this 我的目标是找到一些与此相近的东西 在此输入图像描述 I made that image in Photoshop and yes I know I cant get anywhere close to that result in a easy way, but I want to improve the code in anyway so it looks better. 我在Photoshop中创建了这个图像,是的,我知道我无法以一种简单的方式接近这个结果,但我想要改进代码,所以它看起来更好。 And I dont know how to proceed, any ideas on how to improve is helpful. 而且我不知道如何继续,任何关于如何改进的想法都是有帮助的。

  var colors; window.onload = function () { generatePlanet(); } function generatePlanet() { colors = interpolateColors("rgb(" + getRndColor() + "," + getRndColor() + "," + getRndColor() + ")", "rgb(" + getRndColor() + "," + getRndColor() + "," + getRndColor() + ")", 6000); drawPlanet() } function getRndColor() { return Math.floor(Math.random() * 256) } function interpolateColors(color1, color2, steps) { var stepFactor = 1 / (steps - 1), interpolatedColorArray = []; color1 = color1.match(/\\d+/g).map(Number); color2 = color2.match(/\\d+/g).map(Number); for (var i = 0; i < steps; i++) { interpolatedColorArray.push(interpolateColor(color1, color2, stepFactor * i)); } return interpolatedColorArray; } function interpolateColor(color1, color2, factor) { if (arguments.length < 3) { factor = 0.5; } var result = color1.slice(); for (var i = 0; i < 3; i++) { result[i] = Math.round(result[i] + factor * (color2[i] - color1[i])); } return result; }; function drawPlanet() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var i = 0, j = 0; function animate() { ctx.beginPath(); ctx.fillRect(10 * j, 10 * i, 10, 10); ctx.fillStyle = 'rgb(' + colors[Math.floor(Math.random() * 6001)] + ')'; ctx.fill(); ctx.closePath(); j += 1; if (j >= 70) { i += 1; j = 0; } if (i < 70) { animate(); } } animate(); } 
  #canvas { border: 10px solid #000000; border-radius: 50%; } 
 <button onclick="generatePlanet()">GENERATE</button> <canvas id="canvas" width="700" height="700"></canvas> 

There is comments in the code if you are interested in a random generated planet. 如果您对随机生成的行星感兴趣,则代码中会有注释。 I have not set any color restrictions so some color matches looks a little weird. 我没有设置任何颜色限制,所以一些颜色匹配看起来有点奇怪。 If something is unclear just ask with a comment. 如果有什么不清楚,请询问评论。

 var colors; var tileNum = 0; var tiles; var colorsLand; var colorsWater; var rndLandColor; var rndWaterColor; var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); window.onload = function () { generatePlanet(); } function generatePlanet() { //reset tileNum = 0; tiles = [{ x: 0, y: 0, land: false }]; //Retrive colors colorsLand = interpolateColors("rgb(" + getColor(true) + ")", "rgb(" + getColor(true) + ")", 6000); colorsWater = interpolateColors("rgb(" + getColor(false) + ")", "rgb(" + getColor(false) + ")", 6000); //Creates a array of my tiles and sets either water or land to them and calculates the % of being water/land for (var i = 0; i < 5040; i++) { var currentTile = tiles[tiles.length - 1]; if (currentTile.x <= 69) { var isLand = false; if (currentTile.land == true || tiles.length > 70 && tiles[tiles.length - 70].land == true) { isLand = (Math.floor(Math.random() * 100) + 1) > 35; } else if (currentTile.land == true || tiles.length > 70 && (tiles[tiles.length - 1].land == true || tiles[tiles.length - 70].land == true)) { isLand = (Math.floor(Math.random() * 100) + 1) > 70; } else { isLand = (Math.floor(Math.random() * 100) + 1) > 99; } tiles.push({ x: currentTile.x + 1, y: currentTile.y, land: isLand }); } else { tiles.push({ x: 0, y: currentTile.y + 1, land: isLand }); } } drawPlanet() } //retrive a random color if it's a land tile i want it dark water i want light function getColor(land) { while (true) { var r = Math.floor(Math.random() * 256) + 1 var g = Math.floor(Math.random() * 256) + 1 var b = Math.floor(Math.random() * 256) + 1 hsp = Math.sqrt( 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b) ); //light color if (hsp > 127.5 && land == false) { return r + "," + g + "," + b; } //dark color else if (hsp < 127.5 && land == true) { return r + "," + g + "," + b; } } } //these 2 functions interpolateColor(s) takes 2 colors and gives me 'steps' colors between function interpolateColors(color1, color2, steps) { var stepFactor = 1 / (steps - 1), interpolatedColorArray = []; color1 = color1.match(/\\d+/g).map(Number); color2 = color2.match(/\\d+/g).map(Number); for (var i = 0; i < steps; i++) { interpolatedColorArray.push(interpolateColor(color1, color2, stepFactor * i)); } return interpolatedColorArray; } function interpolateColor(color1, color2, factor) { if (arguments.length < 3) { factor = 0.5; } var result = color1.slice(); for (var i = 0; i < 3; i++) { result[i] = Math.round(result[i] + factor * (color2[i] - color1[i])); } return result; }; //retrives a random color for land function rndLandColor() { return 'rgb(' + colorsLand[Math.floor(Math.random() * 5999) + 1] + ')'; } //retrives a random color for water function rndWaterColor() { return 'rgb(' + colorsWater[Math.floor(Math.random() * 5999) + 1] + ')'; } function drawPlanet() { var i = 0, j = 0; function animate() { ctx.beginPath(); //fill in holes in the land that is bigger then 1 var score = 0; if (tiles[tileNum - 71] !== undefined && tiles[tileNum + 71] !== undefined) { if (tiles[tileNum].land == false) { score++; } if (tiles[tileNum - 1].land == true) { score++; } if (tiles[tileNum + 1].land == true) { score++; } if (tiles[tileNum + 71].land == true) { score++; } if (tiles[tileNum - 71].land == true) { score++; } } if (score >= 3) { ctx.fillStyle = rndLandColor; } //cover single land tiles with water (if land tile is up,down,left and right of this tile) else if ( tiles[tileNum - 71] !== undefined && tiles[tileNum + 71] !== undefined && tiles[tileNum - 1].land == false && tiles[tileNum + 1].land == false && tiles[tileNum - 71].land == false && tiles[tileNum + 71].land == false) { ctx.fillStyle = rndWaterColor(); } //cover single water tiles with land (if water tile is up,down,left and right of this tile) else if ( tiles[tileNum - 71] !== undefined && tiles[tileNum + 71] !== undefined && tiles[tileNum - 1].land == true && tiles[tileNum + 1].land == true && tiles[tileNum - 71].land == true && tiles[tileNum + 71].land == true) { ctx.fillStyle = rndLandColor(); } //cover tile with land else if (tiles[tileNum] !== undefined && tiles[tileNum].land == true) { ctx.fillStyle = rndLandColor(); } //cover tile with water else if (tiles[tileNum] !== undefined && tiles[tileNum].land == false) { ctx.fillStyle = rndWaterColor(); } tileNum++; ctx.fill(); ctx.closePath(); ctx.fillRect(10 * j, 10 * i, 10, 10); j++; if (j >= 71) { i++; j = 0; } if (i <= 71) { animate(); } } animate(); } 
 #canvas { border: 10px solid #000000; border-radius: 50%; background-color: aquamarine; } .container { width: 720px; height: 720px; position: relative; } .gradient { position: absolute; height: 730px; width: 730px; top: 0; left: 0; border-radius: 50%; opacity: 0.8; } 
 <button onclick="generatePlanet()">GENERATE</button> <div class="container"> <img class="gradient" src="https://www.mediafire.com/convkey/1f5a/cgu50lw1ehcp4fq6g.jpg" /> <canvas id="canvas" width="710" height="710"></canvas> </div> 

It's can be more complex task then is looks like. 这可能是更复杂的任务,然后看起来像。 But I can suggest you use next two step way to do it 但我可以建议你使用下一步的方法来做到这一点

  1. some noise algorithm(eg Perlin Noise ) for getting main texture of planet. 获得行星主要纹理的一些噪声算法(例如Perlin Noise )。 It can take you definitely different results with different start parameters. 使用不同的启动参数可以获得明显不同的结果。
  2. Add shadow layer for received from step 2 texture for getting it more realistic. 添加从第2步纹理接收的阴影图层,以使其更逼真。 This can be implemented in two more obvious ways: using predefined shadow texture or using computed shadows in one/several steps. 这可以通过两种更明显的方式实现:使用预定义的阴影纹理或在一个/几个步骤中使用计算的阴影。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM