新聞中心
作者丨ferluht

創(chuàng)新互聯(lián)成立于2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目做網(wǎng)站、成都網(wǎng)站設(shè)計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元湯原做網(wǎng)站,已為上家服務(wù),為湯原各地企業(yè)和個人服務(wù),聯(lián)系電話:18982081108
編譯丨崔皓
策劃丨孫淑娟、梁策
一、簡介
在本文中,我將嘗試簡要概述什么是生成藝術(shù),怎樣將它與 NFT(Non-Fungible Token,非同質(zhì)化代幣)聯(lián)系起來,以及如何在區(qū)塊鏈上制作生成品。蘑菇的設(shè)計將會用到 JavaScript 以及 Three.js,本文將借此向大家介紹如何在虛擬世界生成和發(fā)布藝術(shù)作品。
背景
出于興趣,我常會寫些奇奇怪怪的文章。新年期間,關(guān)于 NFT 的消息一度讓我大為震驚,所以我也想試試在這種模式下玩出一些有創(chuàng)意的東西。我之前不覺得把 JPEG 上傳到區(qū)塊鏈上有什么,但是能夠?qū)⑺囆g(shù)品上鏈的可能性卻讓人滿懷期待。
簡而言之,這背后的思想是制造一些代幣生成器,每次你“鑄造”代幣的時候會給你一個獨特的藝術(shù)品(實際上,是區(qū)塊鏈中的調(diào)用方法,你既需要在其執(zhí)行時花錢,也需要付給藝術(shù)家一定錢款)。很顯然,你可能會有一種奇妙感受,覺得這筆交易生成了一些獨特物品,并永遠存儲在區(qū)塊鏈中。
由此有一些藝術(shù)平臺利用了這個想法,其中最著名的是 artblocks.io。它建立在以太坊區(qū)塊鏈的基礎(chǔ)上,仍然使用工作量證明 (proof-of-work) 模式并且 Gas(手續(xù)費)非常高。所以,我決定嘗試一個更自主、更便宜、更環(huán)保的平臺 - fxhash.xyz。
什么是生成式 NFT 藝術(shù)品?
所有的生成 NFT 藝術(shù)品(NFT,全稱為 Non-Fungible Token,指非同質(zhì)化代幣,包括 jpg 和視頻剪輯形式,是用于表示數(shù)字資產(chǎn)的唯一加密貨幣令牌,可以買賣。)基本上都以網(wǎng)頁方式呈現(xiàn),并使用 vanilla JavaScript 或一些第三方庫在畫布上繪制。NFT 藝術(shù)品可以分為三類:抽象數(shù)學(xué)藝術(shù)品、有形程序藝術(shù)品和可變手繪藝術(shù)品。
如圖 1 所示。
第一類,抽象數(shù)學(xué)藝術(shù)品,利用一些數(shù)學(xué)概念來生成抽象圖像,可能有一些分形(fractals,一種復(fù)雜的幾何形狀)、吸引子(attractors,一個系統(tǒng)有朝某個穩(wěn)態(tài)發(fā)展的趨勢,這個穩(wěn)態(tài)就叫做吸引子)、元胞自動機(cellular automata,是一種時間、空間、狀態(tài)都離散,空間相互作用和時間因果關(guān)系為局部的網(wǎng)格動力學(xué)模型,具有模擬復(fù)雜系統(tǒng)時空演化過程的能力。)等。
第二類,有形程序藝術(shù)品,試圖使用參數(shù)化的方式來描述一些具體的事物。
第三類,可變手繪藝術(shù)品,是對圖像預(yù)先繪制的部分進行簡單隨機化處理。
圖 1
此外,還有一些實驗性和互動性的作品,甚至還有模塊化合成器和游戲之類的藝術(shù)品,不過比較少見。本文將描述一個蘑菇藝術(shù)品的創(chuàng)作過程,并使用交易哈希對其進行隨機化。再加上一些藝術(shù)視野、構(gòu)圖和風(fēng)格化,我們最終創(chuàng)作出了這個“生成式 NFT 藝術(shù)品”。
二、畫蘑菇
Otinium caseubbacula — 生成的蘑菇作品之一
介紹完理論知識,我們進入技術(shù)環(huán)節(jié)。本項目完全使用 Three.js 庫,它有一個使用簡單的 JavaScript 庫,其 API 使用方式很容易在網(wǎng)上找到。
1、生成菌柄
首先要繪制出菌柄的樣條線(我們稱其為基本樣條線,樣條線的作用是輔助生成實體),針對該樣條線進行參數(shù)化形成菌柄的輪廓。為了創(chuàng)建基本樣條線,我使用了 Three.js 中的 CatmullRomCurve3 類。然后,通過沿基本樣條線的移動,創(chuàng)造閉合形狀來構(gòu)建對應(yīng)的幾何圖形,最后將這些圖形連接起來,如圖 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 );
function stipe_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);
}
}
// <- here you need to compute indices of faces
// 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
2、菌柄噪聲
為了讓菌柄看上去更自然,其表面會隨著高度而發(fā)生變化,我們定義了噪聲函數(shù),該函數(shù)對半徑進行定義,通過改變基本樣條曲線上點的角度和相對高度兩個參數(shù)的方式生成不一樣的半徑信息。代碼如下所示:
base_radius = 1; // mean radius
noise_c = 2; // higher this - higher the deformations
// stipe radius as a function of angle and relative position
function stipe_radius(a, t) {
return base_radius + (1 - t)*(1 + Math.random())*noise_c;
菌柄噪聲變化。圖 3
如圖 3 所示,通過半徑的噪聲函數(shù),根據(jù)角度和高度生成不同的半徑。
3、菌帽
菌帽的生成方式也可以通過在菌柄頂部加入旋轉(zhuǎn)的樣條曲線,然后再對曲線進行參數(shù)化來完成。這里可以將旋轉(zhuǎn)產(chǎn)生的表面命名為基礎(chǔ)表面,然后定義基礎(chǔ)表面在基礎(chǔ)樣條上的位置,再加入圍繞菌柄頂部旋轉(zhuǎn)的函數(shù)。這種參數(shù)化的編程方式將方便后面加入噪聲函數(shù)。代碼如下:
adial resolution
cap_cSegments = 20; // angular resolution
cap_points = [];
cap_indices = [];
// cap surface as a function of polar coordinates
function cap_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);
}
}
// <- here you need to compute indices of faces
// 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 所示,通過基礎(chǔ)樣條以及頂點生成基礎(chǔ)平面,這個平面就是菌帽。
菌帽噪聲
同樣為了讓藝術(shù)品看上去更加真實,菌帽也需要加入一些噪聲。我將菌帽噪聲分為三類:徑向噪聲、角度噪聲和法線噪聲。徑向噪聲會影響頂點在基本樣條上的相對位置。角度噪聲改變了圍繞柄頂部基本樣條旋轉(zhuǎn)的角度。最后,法線噪聲會改變頂點沿基面的位置。在坐標(biāo)系中定義菌帽表面時,會對扭曲應(yīng)用 2d Perlin 噪聲,因此這里會使用 noisejs 庫,來完成上述功能。代碼如下:
function radnoise(a, t) {
return -Math.abs(NOISE.perlin2(t * Math.cos(a), t * Math.sin(a)) * 0.5);
}
function angnoise(a, t) {
return NOISE.perlin2(t * Math.cos(a), t * Math.sin(a)) * 0.2;
}
function normnoise(a, t) {
return NOISE.perlin2(t * Math.cos(a), t * Math.sin(a)) * t;
}
function cap_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 所示,從左到右分別給菌帽徑內(nèi)噪聲、角度噪聲和法線噪聲。
蘑菇的其余部分:菌鱗、菌褶、菌環(huán)
菌褶和菌環(huán)的幾何形狀與菌帽的幾何形狀非常相似。可以在帽表面上的一些隨機錨點周圍生成嘈雜的頂點,然后基于它們創(chuàng)建 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);
鱗片、鰓、環(huán)和蘑菇的完整幾何形狀。圖 6
如圖 6 所示,繪制出菌鱗、菌褶、菌環(huán)和蘑菇的完整幾何形狀。
碰撞檢查
由于在同一個場景中會生成多個蘑菇,此時蘑菇之間會產(chǎn)生交叉層疊的情況,所以需要檢查它們之間的碰撞關(guān)系。這里使用一個代碼片段來自檢測每個網(wǎng)格點的光線投射,從而達到檢查蘑菇碰撞的目的。
為了減少計算時間,我在生成這一蘑菇時還生成了其低模。然后使用這個低模來檢查它與其他蘑菇的碰撞。
代碼如下:
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 所示,檢查蘑菇的碰撞效果。
渲染和風(fēng)格化
最初,我想實現(xiàn) 2D 繪圖的效果,雖然所有的生成都是用 3D 制作的。在風(fēng)格化的背景下,我首先想到的是輪廓效果。由于我們不是著色方面的專家,所以只是從這個例子中獲取了輪廓效果,并得到蘑菇輪廓的鉛筆畫風(fēng)格。
圖 8
如圖 8 所示,顯示蘑菇的鉛筆畫風(fēng)格。
接著就是著色了,蘑菇的紋理應(yīng)該有噪點并且有一些柔和的陰影。如果不想處理 UV 的話,簡便的方式就是使用 BufferGeometry API 定義對象的頂點顏色。使用這種方法還可以將頂點的顏色進行參數(shù)化,也就是加入角度和位置作為參數(shù)從而改變顏色,因此噪聲紋理的生成變得稍微容易一些了。
圖 9
如圖 9 所示,通過角度和位置給頂點添加顏色。
最后,使用 EffectComposer 添加了一些全局噪聲和類似膜顆粒效果(Film Grain 是模擬照相膜的隨機光學(xué)紋理)。代碼如下:
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);
// <- create scene and camera
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();
幾乎準(zhǔn)備好了,彩色和有噪點的蘑菇。圖 10
如圖 10 所示,即將要完成的蘑菇。
生成名字
有關(guān)名字生成,本文使用了一個簡單的馬爾可夫鏈,它接受了 1k 個蘑菇名稱的訓(xùn)練。為了預(yù)處理和標(biāo)記這些名稱,我使用了 Python 庫 YouTokenToMe。有了它,可以將所有名稱拆分為 200 個唯一標(biāo)記,并將它們的轉(zhuǎn)換概率寫入 JavaScript 字典。代碼的 JS 端只讀取這些概率并堆疊標(biāo)記,直到它生成幾個單詞。
以下是使用這種方法生成的一些蘑菇名稱示例:
Stricosphaete cinus
Fusarium sium confsisomyc
Etiformansum poonic
Hellatatum bataticola
Armillanata gossypina mortic
Chosporium anniiffact
Fla po sporthrina
三、完成
圖 11
如圖 11 所示,在 fxhash 上鑄造的 15 個蘑菇。
準(zhǔn)備發(fā)布
首先,要準(zhǔn)備一個項目在 fxhash 上發(fā)布,只需將代碼中的所有隨機調(diào)用更改為 fxrand() 方法。主要思想是必須為每個哈希生成唯一的輸出,需要對相同的哈希生成完全相同的輸出。然后在沙箱中測試代幣,最后在鑄幣過程開啟時鑄幣。
于是我們的《蘑菇圖集》就誕生了。你可以在此處查看并關(guān)注它的變化。雖然它不像之前的一些作品那樣很快售罄,但我認為這是自己藝術(shù)生涯中最前沿也最具挑戰(zhàn)性的作品。希望鑄造這個代幣的人也能在非同質(zhì)化代幣的世界中領(lǐng)略不同質(zhì)的美!
網(wǎng)站題目:怎樣用Three.js畫一個NFT“蘑菇”
當(dāng)前地址:http://m.fisionsoft.com.cn/article/dpooigi.html


咨詢
建站咨詢
