
如何使用Three.js 繪制NFT蘑菇窗體頂端
#第 1 部分:簡介
在本文中,我將嘗試簡要而完整地概述如何創造藝術品,并將你的藝術品發布到NFT(Non-Fungible Token非同質化代幣)網站進行售賣。以及如何在區塊鏈上制作藝術品。藝術品:蘑菇的設計將會使用到JavaScript以及Three.js,本文將告訴大家在虛擬世界如何生成和發布藝術品。
#背景
出于興趣筆者會編寫一些不尋常的東西。就在新年期間,關于 NFT 的消息讓筆者感到震驚,這也是為什么會嘗試在這種模式下創造一些有創意的東西。雖說將 JPEG 上傳到區塊鏈的想法并不新奇,但是能夠將藝術品上鏈的可能性卻讓人滿懷期待。
簡而言之,它背后的想法是通過代幣生成器,每次你“鑄造”代幣的時候會售賣給你一個獨特的藝術品(實際上,調用區塊鏈方法花費你的錢購買藝術家創造的藝術品)。毫無疑問,這筆交易會產生一個獨特的對象,并永遠存儲在區塊鏈中,不可否認這種交易具備一種魔力,不是嗎?
由此有一些藝術平臺利用了這個想法,其中最著名的是artblocks.io。但它建立在太坊區塊鏈的基礎上,它仍然使用proof-of-work(工作量證明)的模式并且Gas(手續費)非常高,于是筆者決定嘗試一個更民主、更便宜、環保平臺 - fxhash.xyz
#什么是生成式 NFT 藝術品?
所有的生成 NFT (NFT,全稱為Non-Fungible Token,指非同質化代幣,包括jpg和視頻剪輯形式,是用于表示數字資產,的唯一加密貨幣令牌,可以買賣。)NFT的藝術品基本上都以網頁的方式呈現,并使用 vanilla JavaScript或一些第三方庫在畫布上繪制藝術品。NFT藝術品可以分為 3 類:抽象數學藝術品、有形程序藝術品和可變手繪藝術品。
如圖1所示,
第一類,抽象數學,利用一些數學概念來生成抽象圖像:可能有一些分形(fractals 一種復雜的幾何形狀)、吸引子(attractors,一個系統有朝某個穩態發展的趨勢,這個穩態就叫做吸引子)、元胞自動機(cellular automata,是一種時間、空間、狀態都離散,空間相互作用和時間因果關系為局部的網格動力學模型,具有模擬復雜系統時空演化過程的能力。
)等。
第二類,有形程序藝術品,試圖使用參數化的方式來描述一些具體的事物。
第三類,可變手繪藝術品,是對圖像預先繪制的部分進行簡單隨機化處理。
圖1
此外,還有一些實驗性和互動性的作品,甚至還有模塊化合成器和游戲之類的藝術品,不過比較少見。
文章描述一個蘑菇(藝術品)的創作過程,并使用事務哈希對其進行隨機化。結合藝術視野、構圖和風格化等手段,創作了這個“生成式 NFT 藝術品”。
#第 2 部分:畫蘑菇
Otinium caseubbacula — 生殖蘑菇標本之一
在介紹王理論知識之后,讓我們進入技術環節。本項目完全使用Three.js庫,它有一個使用簡單的JavaScript庫,在網上很容易查找到它的API使用方式。
#生成菌柄
首先要繪制出菌柄的樣條線(我們稱其為基本樣條線,樣條線的作用是輔助生成實體),針對該樣條線進行參數化形成菌柄的輪廓。為了創建基本樣條線,使用了 Three.js中的CatmullRomCurve3類。然后,通過沿基本樣條線的移動,創造閉合形狀來構建對應的幾何圖形,最后將這些圖形連接起來,如圖2所示。為此使用了Three.js中的 BufferGeometry組件。
代碼入如下:
stipe_vSegments =30;// vertical resolution
stipe_rSegments =20;// angular resolution
stipe_points =[];// vertices
stipe_indices =[];// face indices
stipe_shape =new THREE.CatmullRomCurve3(..., closed=false);
functionstipe_radius(a, t){...}
for(var t =0; t <1; t +=1/ stipe_vSegments){
// stipe profile curve
var curve =new THREE.CatmullRomCurve3([
new THREE.Vector3(0,0,stipe_radius(0, t)),
new THREE.Vector3(stipe_radius(Math.PI /2, t),0,0),
new THREE.Vector3(0,0,-stipe_radius(Math.PI, t)),
new THREE.Vector3(-stipe_radius(Math.PI *1.5, t),0,0),
], closed=true, curveType='catmullrom', tension=0.75);
var profile_points = curve.getPoints( stipe_rSegments );
for(var i =0; i < profile_points.length; i++){
stipe_points.push(profile_points[i].x, profile_points[i].y, profile_points[i].z);
}
}
//
// and then create a BufferGeometry
var stipe =new THREE.BufferGeometry();
stipe.setAttribute('position',new THREE.BufferAttribute(new Float32Array(stipe_points),3));
stipe.setIndex(stipe_indices);
stipe.computeVertexNormals();
菌柄生成的階段:樣條、頂點、面
圖2
#菌柄噪聲
為了讓菌柄看上去更自然,其表面會隨著高度而發生變化,我們定義了噪聲函數,該函數對半徑進行定義,通過改變基本樣條曲線上點的角度和相對高度兩個參數的方式生成不一樣的半徑信息。如下代碼所示。
base_radius =1;// mean radius
noise_c =2;// higher this - higher the deformations
// stipe radius as a function of angle and relative position
functionstipe_radius(a, t){
return base_radius +(1- t)*(1+ Math.random())*noise_c;
}
菌柄噪聲變化
圖3
如圖3所示,通過半徑的噪聲函數,根據角度和高度生成不同的半徑。
#菌帽
菌帽的生成方式,也可以通過在菌柄頂部加入旋轉的樣條曲線,然后再對曲線進行參數化來完成。這里可以可以將旋轉產生的表面命名為基礎表面,然后定義基礎表面在基礎樣條上的位置再加入圍繞菌柄頂部旋轉的函數。這種參數化的編程方式將方便后面加入噪聲函數。代碼如下:
adial resolution
cap_cSegments =20;// angular resolution
cap_points =[];
cap_indices =[];
// cap surface as a function of polar coordinates
functioncap_surface(a0, t0){
// 1. compute (a,t) from (a0,t0), e.g apply noise
// 2. compute spline value in t
// 3. rotate it by angle a around stipe end
// 4. apply some other noises/transformations
...
return surface_point;
}
// spawn surface vertices with resolution
// cap_rSegments * cap_cSegments
for(var i =1; i cap_rSegments; i++){
var t0 = i / cap_rSegments;
for(var j =0; j < cap_cSegments; j++){
var a0 = Math.PI *2/ cap_cSegments * j;
var surface_point =cap_surface(a0, t0);
cap_points.push(surface_point.x, surface_point.y, surface_point.z);
}
}
//
// and then create a BufferGeometry
var cap =new THREE.BufferGeometry();
cap.setAttribute('position',new THREE.BufferAttribute(new Float32Array(cap_points),3));
cap.setIndex(cap_indices);
cap.computeVertexNormals();
帽生成階段:樣條、頂點、面
圖4
如圖4所示,通過基礎樣條以及頂點生成基礎平面,這個平明就是菌帽。
#菌帽噪音
同樣為了讓藝術品看上去更加真實,菌帽也需要加入一些噪音。我將帽噪聲分為 3類:徑向噪聲、角度噪聲和法線噪聲。徑向噪聲會影響頂點在基本樣條上的相對位置。角噪聲改變了圍繞柄頂部基本樣條旋轉的角度。
最后,法線噪聲會改變頂點沿基面的位置。在坐標系中定義帽表面時,會對扭曲應用 2d Perlin 噪聲,因此這里會使用noisejs庫,來完成上述功能。代碼如下:
functionradnoise(a, t){
return-Math.abs(NOISE.perlin2(t * Math.cos(a), t * Math.sin(a))*0.5);
}
functionangnoise(a, t){
return NOISE.perlin2(t * Math.cos(a), t * Math.sin(a))*0.2;
}
functionnormnoise(a, t){
return NOISE.perlin2(t * Math.cos(a), t * Math.sin(a))* t;
}
functioncap_surface(a0, t0){
// t0 -> t by adding radial noise
var t = t0 *(1+radnoise(a, t0));
// compute normal vector in t
var shape_point = cap_shape.getPointAt(t);
var tangent = cap_shape.getTangentAt(t);
var norm =new THREE.Vector3(0,0,0);
const z1 =new THREE.Vector3(0,0,1);
norm.crossVectors(z1, tangent);
// a0 -> a by adding angular noise
var a =angnoise(a0, t);
var surface_point =new THREE.Vector3(
Math.cos(a)* shape_point.x,
shape_point.y,
Math.sin(a)* shape_point.x
);
// normal noise coefficient
var surfnoise_val =normnoise(a, t);
// finally surface point
surface_point.x += norm.x * Math.cos(a)* surfnoise_val;
surface_point.y += norm.y * surfnoise_val;
surface_point.z += norm.x * Math.sin(a)* surfnoise_val;
return surface_point;
}
從左到右的噪聲分量:徑向、角度、法線
圖5
如圖5所示,從左到右分別給菌帽徑內噪聲、角度噪聲和法線噪聲。
#蘑菇的其余部分:鱗片、鰓、環
鰓和環的幾何形狀與帽的幾何形狀非常相似。可以在帽表面上的一些隨機錨點周圍生成嘈雜的頂點,然后基于它們創建ConvexGeometry 。代碼如下:
bufgeoms =[];
scales_num =20;
n_vertices =10;
scale_radius =2;
for(var i =0; i < scales_num; i++){
var scale_points =[];
// choose a random center of the scale on the cap
var a = Math.random()* Math.PI *2;
var t = Math.random();
var scale_center =cap_surface(a, t);
// spawn a random point cloud around the scale_center
for(var j =0; j < n_vertices; j++){
scale_points.push(new THREE.Vector3(
scale_center.x +(1- Math.random()*2)* scale_radius,
scale_center.y +(1- Math.random()*2)* scale_radius,
scale_center.z +(1- Math.random()*2)* scale_radius
);
}
// create convex geometry using these points
var scale_geometry =new THREE.ConvexGeometry( scale_points );
bufgeoms.push(scale_geometry);
}
// join all these geometries into one BufferGeometry
var scales = THREE.BufferGeometryUtils.mergeBufferGeometries(bufgeoms);
鱗片、鰓、環和蘑菇的完整幾何形狀
圖6
如圖6所示,繪制出鱗片、鰓、環和蘑菇的完整幾何形狀。
#碰撞檢查
由于在同一個場景中會生成多個蘑菇,此時蘑菇之間會產生交叉層疊的情況,所以需要檢查它們之間的碰撞關系。這里使用一個代碼片段來自檢測每個網格點的光線投射,從而達到檢查蘑菇碰撞的目的。
為了減少計算時間,生成了蘑菇的低多邊形雙胞胎以及蘑菇本身。然后使用這個低多邊形模型來檢查與其他蘑菇的碰撞。
代碼如下:
for(var vertexIndex =0; vertexIndex < Player.geometry.attributes.position.array.length; vertexIndex++)
{
var localVertex =new THREE.Vector3().fromBufferAttribute(Player.geometry.attributes.position, vertexIndex).clone();
var globalVertex = localVertex.applyMatrix4(Player.matrix);
var directionVector = globalVertex.sub( Player.position );
var ray =new THREE.Raycaster( Player.position, directionVector.clone().normalize());
var collisionResults = ray.intersectObjects( collidableMeshList );
if( collisionResults.length >0&& collisionResults[0].distance < directionVector.length())
{
// a collision occurred... do something...
}
}
用于更快碰撞檢查的簡化模型
圖7
如圖7所示,檢查蘑菇的碰撞效果
#渲染和風格化
最初,想實現 2d 繪圖的效果,盡管所有的生成都是用 3d 制作的。在風格化的背景下,首先想到的是輪廓效果。由于我們不是著色器方面的專家,所以只是從這個例子中獲取了輪廓效果,并得到蘑菇輪廓的鉛筆畫風格。
三個js輪廓效果
圖8
如圖8所示,顯示蘑菇的鉛筆畫風格。
接著就是著色了,蘑菇的紋理應該有點嘈雜并且有一些柔和的陰影。這里有一個簡便的方式就是使用BufferGeometry API定義對象的頂點顏色。使用這種方法還可以將頂點的顏色進行參數化,也就是加入角度和位置作為參數從而改變顏色,因此噪聲紋理的生成變得稍微容易一些了。
添加一些頂點顏色
圖9
如圖9所示,通過角度和位置給頂點添加顏色。
最后,使用EffectComposer添加了一些全局噪聲和類似膜顆粒效果(Film Grain是模擬照相膜的隨機光學紋理)。代碼如下:
var renderer =new THREE.WebGLRenderer({antialias:true});
outline =new THREE.OutlineEffect( renderer ,{thickness:0.01, alpha:1, defaultColor:[0.1,0.1,0.1]});
var composer =new THREE.EffectComposer(outline);
//
var renderPass =new THREE.RenderPass( scene, camera );
composer.addPass( renderPass );
var filmPass =new THREE.FilmPass(
0.20,// noise intensity
0.025,// scanline intensity
648,// scanline count
false,// grayscale
);
composer.addPass(filmPass);
composer.render();
幾乎準備好了,彩色和嘈雜的蘑菇
圖10
如圖10 所示,即將要完成的蘑菇。
#起個名字
對于起名字這件事,使用了一個簡單的馬爾可夫鏈,它接受了 1k 個蘑菇名稱的訓練。為了預處理和標記這些名稱,使用了 python 庫YouTokenToMe。有了它,可以將所有名稱拆分為 200 個唯一標記,并將它們的轉換概率寫入JavaScript字典。代碼的 JS 端只讀取這些概率并堆疊標記,直到它生成幾個單詞。
以下是使用這種方法生成的一些蘑菇名稱示例:
Stricosphaete cinus
Fusarium sium confsisomyc
Etiformansum poonic
Hellatatum bataticola
Armillanata gossypina mortic
Chosporium anniiffact
Fla po sporthrina
#第 3 部分:完成
圖11
如圖11所示,在 fxhash 上鑄造的 15 個蘑菇
#準備發布
首先,要準備一個項目在 fxhash 上發布,只需將代碼中的所有隨機調用更改為fxrand()方法。主要思想是必須為每個哈希生成唯一的輸出,需要對相同的哈希生成完全相同的輸出。然后在沙箱中測試令牌,最后在打開鑄幣時鑄幣。而已!
這將我們帶到了蘑菇地圖集(我將這個集合命名為)。您可以在此處查看并查看其變化。雖然它不像筆者之前的一些作品那樣售罄,但筆者認為這是他藝術生涯中最先進和最具挑戰性的作品。希望鑄造這個代幣的人也能在NFT的世界中享受他們的真菌!
原文地址:https://hackernoon.com/how-to-draw-generative-nft-mushrooms-with-threejs