Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
本文中,我們將了解如何使用超過 150 行代碼將圖像渲染到頁面!我知道可以只使用一個(gè)<img>標(biāo)簽并完成它。但這樣做是一個(gè)很好的練習(xí),因?yàn)樗仁刮覀円朐S多重要的 WebGL 概念。
我最近在一個(gè)需要使用 WebGL 的項(xiàng)目上工作。我試圖在瀏覽器中的地圖上渲染數(shù)千個(gè)多邊形,但結(jié)果證明 GeoJSON 太慢了。為了加快速度,我想盡可能降低到最低水平,并使用 WebGL 和著色器實(shí)際編寫可以直接在 GPU 上運(yùn)行的代碼。我一直想了解著色器,但一直沒有機(jī)會(huì),所以這是一個(gè)在解決非常具體的技術(shù)挑戰(zhàn)的同時(shí)學(xué)習(xí)新東西的好機(jī)會(huì)。
起初,我很難弄清楚我需要做什么。復(fù)制和粘貼示例代碼通常不起作用,而且我并沒有真正了解如何從示例轉(zhuǎn)到我需要的自定義解決方案。然而,一旦我完全理解了這一切是如何結(jié)合在一起的,它突然在我腦海中響起,結(jié)果證明解決方案非常簡單。最困難的部分是圍繞一些概念思考。所以,我想寫一篇文章解釋我學(xué)到了什么,幫助你理解這些概念,并希望讓你更容易編寫你的第一個(gè)著色器。
以下是我們將在本文中執(zhí)行的操作:
希望你可以使用這些概念作為起點(diǎn),使用 WebGL 做一些非常酷且有用的事情。
即使你最終使用庫來幫助編寫 WebGL 代碼,我發(fā)現(xiàn)了解幕后的原始 API 調(diào)用對(duì)于了解實(shí)際發(fā)生的情況很有用,尤其是在出現(xiàn)問題時(shí)。
要在瀏覽器中使用 WebGL,你需要向<canvas>頁面添加標(biāo)簽。使用畫布,你可以使用 2D Canvas API 進(jìn)行繪制,也可以選擇使用 3D WebGL API版本 1 或 2。我實(shí)際上并不了解 WebGL 1 和 2 之間的區(qū)別,但我會(huì)希望有一天能了解更多。我將在這里討論的代碼和概念適用于這兩個(gè)版本。
如果想讓你的畫布填充視口,你可以從這個(gè)簡單的 HTML 開始:
<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>WebGL</title>
<style>
html, body, canvas {
width: 100%;
height: 100%;
border: 0;
padding: 0;
margin: 0;
position: absolute;
}
</style>
<body>
<canvas></canvas>
<script></script>
</body>
</html>
這會(huì)給你一個(gè)空白的、白色的、無用的頁面。你需要一些 JavaScript 來實(shí)現(xiàn)它。在<script>標(biāo)簽內(nèi),添加這些行以訪問畫布的 WebGL API:
const canvas=document.querySelector('canvas');
const gl=canvas.getContext('webgl');
WebGL 基于 OpenGL,并使用相同的著色器語言。沒錯(cuò),著色器程序(Shader)是用他們自己的語言 GLSL 編寫的,它代表圖形庫著色器語言。
GLSL 讓我想起了 C 或 JavaScript,但它有自己的特點(diǎn),局限性非常大,但也非常強(qiáng)大。很酷的一點(diǎn)是,它直接在 GPU 上而不是在 CPU 上運(yùn)行。因此它可以非常快速地完成普通 CPU 程序無法完成的事情。它針對(duì)使用向量和矩陣處理數(shù)學(xué)運(yùn)算進(jìn)行了優(yōu)化。
我們需要兩種類型的著色器:頂點(diǎn)(Vertex)著色器和片段(Fragment)著色器。頂點(diǎn)著色器可以進(jìn)行計(jì)算以確定每個(gè)頂點(diǎn)(三角形的角)的位置。片段著色器計(jì)算出如何為三角形內(nèi)的每個(gè)片段(像素)著色。
這兩個(gè)著色器相似,但在不同的時(shí)間做不同的事情。頂點(diǎn)著色器首先運(yùn)行,以確定每個(gè)三角形的去向,然后它可以將一些信息傳遞給片段著色器,因此片段著色器可以計(jì)算出如何繪制每個(gè)三角形。
這是一個(gè)基本的頂點(diǎn)著色器,它將接收一個(gè)帶有 x,y 坐標(biāo)的向量。向量基本上只是一個(gè)具有固定長度的數(shù)組。vec2是有 2 個(gè)數(shù)字的數(shù)組,vec4是有 4 個(gè)數(shù)字的數(shù)組。所以,這個(gè)程序?qū)⒉捎靡粋€(gè)全局“屬性”變量,一個(gè)名為“points”的 vec2(這是我編的一個(gè)名字)。
然后它會(huì)告訴 GPU,這正是頂點(diǎn)將要去的地方,方法是將它分配給另一個(gè)內(nèi)置于 GLSL 中的名為gl_Position 的變量。
它將針對(duì)三角形的每個(gè)頂點(diǎn)運(yùn)行,并且每次points都有不同的 x,y 值。稍后你將看到我們?nèi)绾味x和傳遞這些坐標(biāo)。
這是我們的第一個(gè)“你好,世界!” 頂點(diǎn)著色器程序:
attribute vec2 points;
void main(void) {
gl_Position=vec4(points, 0.0, 1.0);
}
此處不涉及任何計(jì)算,只是我們需要將 vec2 轉(zhuǎn)換為 vec4。前兩個(gè)數(shù)字是 x 和 y,第三個(gè)是 z,我們只需將其設(shè)置為 0.0,因?yàn)槲覀冋诶L制二維圖片,我們不需要擔(dān)心第三維。我不知道第四個(gè)值是什么意思,但我們只是將其設(shè)置為 1.0。根據(jù)我的閱讀,我認(rèn)為這與使矩陣數(shù)學(xué)更容易有關(guān)。
我喜歡 GLSL 中的這一點(diǎn),向量是一種基本數(shù)據(jù)類型,你可以使用其他向量輕松創(chuàng)建向量。我們可以這樣寫上面的行:
gl_Position=vec4(points[0], points[1], 0.0, 1.0);
但相反,我們能夠使用快捷方式,只需將 vec2 點(diǎn)作為第一個(gè)參數(shù)傳入,GLSL 就知道該怎么做。它讓我想起了在 JavaScript 中使用擴(kuò)展運(yùn)算符:
// javascript
gl_Position=[...points, 0.0, 1.0];
因此,如果角形角之一的 x 為 0.2,y 為 0.3,我們的代碼將有效地執(zhí)行以下操作:
gl_Position=vec4(0.2, 0.3, 0.0, 1.0);
但是我們不能像這樣將 x 和 y 坐標(biāo)硬編碼到我們的程序中,否則所有的三角形都只是屏幕上的一個(gè)點(diǎn)。我們使用屬性向量(Attribute)代替,以便每個(gè)角(或頂點(diǎn))可以位于不同的位置。
頂點(diǎn)著色器為每個(gè)三角形的每個(gè)角運(yùn)行一次,而片段著色器為每個(gè)三角形內(nèi)的每個(gè)彩色像素運(yùn)行一次。
頂點(diǎn)著色器使用名為 gl_Position的全局 vec4 變量定義每個(gè)頂點(diǎn)的位置,而片段著色器通過使用名為gl_FragColor 的局 vec4 變量定義每個(gè)像素的顏色。以下是我們?nèi)绾斡眉t色像素填充所有三角形:
void main() {
gl_FragColor=vec4(1.0, 0.0, 0.0, 1.0);
}
這里顏色向量是 RGBA,因此紅色、綠色、藍(lán)色和 alpha 中的每一個(gè)都是介于 0 和 1 之間的數(shù)字。所以上面的例子只是將每個(gè)片段或像素設(shè)置為完全不透明的亮紅色。
你通常不會(huì)用純色填充所有三角形,因此,我們希望片段著色器引用圖像(或“紋理”)并為三角形內(nèi)的每個(gè)像素提取正確的顏色。
我們需要使用顏色信息訪問紋理,以及一些告訴我們圖像如何映射到形狀上的“紋理坐標(biāo)”。
首先,我們將修改頂點(diǎn)著色器以訪問坐標(biāo)并將它們傳遞給片段著色器:
attribute vec2 points;
attribute vec2 texture_coordinate;
varying highp vec2 v_texture_coordinate;
void main(void) {
gl_Position=vec4(points, 0.0, 1.0);
v_texture_coordinate=texture_coordinate;
}
如果你像我一樣,可能會(huì)擔(dān)心會(huì)需要各種瘋狂的三角函數(shù),但別擔(dān)心 - 事實(shí)證明這是最簡單的部分,這要?dú)w功于 GPU 的魔力。
我們?yōu)槊總€(gè)頂點(diǎn)獲取一個(gè)紋理坐標(biāo),然后將其傳遞給變量中的片段著色器,該varying變量將“插入”每個(gè)片段或像素的坐標(biāo)。這本質(zhì)上是兩個(gè)維度的百分比,因此對(duì)于三角形內(nèi)的任何特定像素,我們將準(zhǔn)確知道要選擇圖像的哪個(gè)像素。
圖像存儲(chǔ)在一個(gè)名為 sampler的二維采樣器變量中。我們從頂點(diǎn)著色器接收varying紋理坐標(biāo),并使用GLSL 函數(shù)texture2D從紋理中采樣適當(dāng)?shù)膯蝹€(gè)像素。
這聽起來很復(fù)雜,但由于 GPU 的魔力,它變得非常簡單。我們需要做任何數(shù)學(xué)運(yùn)算的唯一部分是將三角形的每個(gè)頂點(diǎn)坐標(biāo)與圖像的坐標(biāo)相關(guān)聯(lián),稍后我們將看到它變得非常簡單。
precision highp float;
varying highp vec2 v_texture_coordinate;
uniform sampler2D sampler;
void main() {
gl_FragColor=texture2D(sampler, v_texture_coordinate);
}
我們剛剛研究了如何使用 GLSL 編寫兩個(gè)不同的著色器,但我們還沒有討論過如何在 JavaScript 中做到這一點(diǎn)。只需要將這些 GLSL 著色器轉(zhuǎn)換為 JavaScript 字符串,然后我們就可以使用 WebGL API 編譯它們并將它們放在 GPU 上。
有些人喜歡把shader源代碼直接放在HTML中使用<script type="x-shader/x-vertex">之類的script標(biāo)簽,然后用.innerText把代碼拉出來。你還可以將著色器放入單獨(dú)的文本文件中并使用fetch 加載 。具體怎么做取決于你。
我發(fā)現(xiàn)直接在 JavaScript 中使用模板字符串編寫著色器源代碼是最簡單的。看起來是這樣的:
const vertexShaderSource=`
attribute vec2 points;
attribute vec2 texture_coordinate;
varying highp vec2 v_texture_coordinate;
void main(void) {
gl_Position=vec4(points, 0.0, 1.0);
v_texture_coordinate=texture_coordinate;
}
`;
const fragmentShaderSource=`
precision highp float;
varying highp vec2 v_texture_coordinate;
uniform sampler2D sampler;
void main() {
gl_FragColor=texture2D(sampler, v_texture_coordinate);
}
`;
接下來,我們需要?jiǎng)?chuàng)建一個(gè) GL“程序”并將這兩個(gè)不同的著色器添加到其中,如下所示:
// create a program (which we'll access later)
const program=gl.createProgram();
// create a new vertex shader and a fragment shader
const vertexShader=gl.createShader(gl.VERTEX_SHADER);
const fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);
// specify the source code for the shaders using those strings
gl.shaderSource(vertexShader, vertexShaderSource);
gl.shaderSource(fragmentShader, fragmentShaderSource);
// compile the shaders
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// attach the two shaders to the program
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
最后,我們必須告訴 GL 鏈接并使用我們剛剛創(chuàng)建的程序。請(qǐng)注意,一次只能使用一個(gè)程序:
gl.linkProgram(program);
gl.useProgram(program);
如果程序出現(xiàn)問題,我們應(yīng)該將錯(cuò)誤記錄到控制臺(tái)。否則,它將默默地失敗:
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
}
如您所見,WebGL API 非常冗長。但是,如果仔細(xì)查看這些代碼,會(huì)發(fā)現(xiàn)它們并沒有做任何太令人驚訝的事情。這些代碼塊非常適合復(fù)制和粘貼,因?yàn)楹茈y記住它們,而且它們很少更改。你可能需要更改的唯一部分是模板字符串中的著色器源代碼。
現(xiàn)在我們的程序已經(jīng)全部連接好,是時(shí)候給它一些坐標(biāo)并讓它在屏幕上繪制一些三角形了!
首先,我們需要了解 WebGL 的默認(rèn)坐標(biāo)系。它與屏幕上的常規(guī)像素坐標(biāo)系完全不同。在 WebGL 中,畫布的中心是 0,0,左上角是 -1,-1,右下角是 1,1。
如果我們想渲染一張照片,需要一個(gè)矩形。但是 WebGL 只知道如何繪制三角形。那么我們?nèi)绾问褂萌切卫L制一個(gè)矩形呢?我們可以使用兩個(gè)三角形來創(chuàng)建一個(gè)矩形。我們將有一個(gè)三角形覆蓋左上角,另一個(gè)覆蓋右下角,如下所示:
要繪制三角形,需要指定每個(gè)三角形三個(gè)角的坐標(biāo)。讓我們創(chuàng)建一個(gè)數(shù)字?jǐn)?shù)組。兩個(gè)三角形的 x 和 y 坐標(biāo)都將在一個(gè)數(shù)組中,如下所示:
const points=[
// first triangle
// top left
-1, -1,
// top right
1, -1,
// bottom left
-1, 1,
// second triangle
// bottom right
1, 1,
// top right
1, -1,
// bottom left
-1, 1,
];
要將數(shù)字列表傳遞到我們的著色器程序中,我們必須創(chuàng)建一個(gè)“緩沖區(qū)”,然后將一個(gè)數(shù)組加載到緩沖區(qū)中,然后告訴 WebGL 將緩沖區(qū)中的數(shù)據(jù)用于我們的著色器程序中的屬性。
我們不能只將 JavaScript 數(shù)組加載到 GPU 中,它必須是嚴(yán)格類型的。所以我們把它包裝在一個(gè)Float32Array 中。 我們也可以使用整數(shù)或任何對(duì)我們的數(shù)據(jù)有意義的類型,但對(duì)于坐標(biāo),浮點(diǎn)數(shù)最有意義。
// create a buffer
const pointsBuffer=gl.createBuffer();
// activate the buffer, and specify that it contains an array
gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer);
// upload the points array to the active buffer
// gl.STATIC_DRAW tells the GPU this data won't change
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);
請(qǐng)記住,我在著色器程序的頂部創(chuàng)建了一個(gè)名為“points”的屬性,帶有attribute vec2 points;? 現(xiàn)在我們的數(shù)據(jù)在緩沖區(qū)中,并且緩沖區(qū)處于活動(dòng)狀態(tài),我們可以用需要的坐標(biāo)填充那個(gè)“points”屬性:
// get the location of our "points" attribute in our shader program
const pointsLocation=gl.getAttribLocation(program, 'points');
// pull out pairs of float numbers from the active buffer
// each pair is a vertex that will be available in our vertex shader
gl.vertexAttribPointer(pointsLocation, 2, gl.FLOAT, false, 0, 0);
// enable the attribute in the program
gl.enableVertexAttribArray(pointsLocation);
在 WebGL 中,紋理是一種在網(wǎng)格中提供大量數(shù)據(jù)的方法,這些數(shù)據(jù)可用于將像素繪制到形狀上。圖像是一個(gè)明顯的例子,它們是沿行和列的紅色、藍(lán)色、綠色和 alpha 值的網(wǎng)格。但是,你可以將紋理用于根本不是圖像的事物。就像計(jì)算機(jī)中的所有信息一樣,它最終只是數(shù)字列表。
由于我們?cè)跒g覽器中,我們可以使用常規(guī)的 JavaScript 代碼來加載圖像。加載圖像后,我們將使用它來填充紋理。
在我們執(zhí)行任何 WebGL 代碼之前先加載圖像可能是最簡單的,然后在圖像加載后運(yùn)行整個(gè) WebGL 初始化的東西,所以我們不需要等待任何東西,像這樣:
const img=new Image();
img.src='photo.jpg';
img.onload=()=> {
// assume this runs all the code we've been writing so far
initializeWebGLStuff();
};
現(xiàn)在我們的圖像已經(jīng)加載,我們可以創(chuàng)建一個(gè)紋理并將圖像數(shù)據(jù)上傳到其中。
// create a new texture
const texture=gl.createTexture();
// specify that our texture is 2-dimensional
gl.bindTexture(gl.TEXTURE_2D, texture);
// upload the 2D image (img) and specify that it contains RGBA data
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
由于我們的圖像可能不是2的N次冪,因此還必須告訴 WebGL 在放大或縮小圖像時(shí)如何選擇要繪制的像素,否則會(huì)拋出錯(cuò)誤。
// tell WebGL how to choose pixels when drawing our non-square image
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// bind this texture to texture #0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
最后,我們想在著色器程序中訪問這個(gè)紋理。我們用代碼uniform sampler2D sampler;定義了一個(gè)二維 uniform變量,告訴 GPU 應(yīng)該使用我們的新紋理。
// use the texture for the uniform in our program called "sampler",
gl.uniform1i(gl.getUniformLocation(program, 'sampler'), 0);
我們快完成了!下一步非常重要。我們需要告訴著色器我們的圖像應(yīng)該如何繪制到三角形上。我們希望將圖像的左上角繪制在左上三角形的左上角。等等。
圖像紋理的坐標(biāo)系與我們使用的三角形不同,所以我們必須考慮一下,不幸的是不能只使用完全相同的坐標(biāo)。以下是它們的不同之處:
紋理坐標(biāo)應(yīng)該與我們的三角形頂點(diǎn)坐標(biāo)的順序完全相同,因?yàn)檫@就是它們?cè)陧旤c(diǎn)著色器中一起顯示的方式。當(dāng)我們的頂點(diǎn)著色器為每個(gè)頂點(diǎn)運(yùn)行時(shí),它還能夠訪問每個(gè)紋理坐標(biāo),并將其作為varying變量傳遞給片段著色器。
我們將使用與上傳三角坐標(biāo)數(shù)組幾乎相同的代碼,只是現(xiàn)在我們將把它與名為“texture_coordinate”的屬性相關(guān)聯(lián)。
const textureCoordinates=[
// first triangle
// top left
0, 1,
// top right
1, 1,
// bottom left
0, 0,
// second triangle
// bottom right
1, 0,
// top right
1, 1,
// bottom left
0, 0,
];
// same stuff we did earlier, but passing different numbers
const textureCoordinateBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordinateBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gl.STATIC_DRAW);
// and associating it with a different attribute
const textureCoordinateLocation=gl.getAttribLocation(program, 'texture_coordinate');
gl.vertexAttribPointer(textureCoordinateLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(textureCoordinateLocation);
現(xiàn)在我們已經(jīng)將著色器、所有坐標(biāo)和圖像加載到 GPU 中,我們已經(jīng)準(zhǔn)備好實(shí)際運(yùn)行著色器程序并讓它將我們的圖像繪制到畫布上。
為此,我們只需要一行代碼:
gl.drawArrays(gl.TRIANGLES, 0, 6);
這告訴 WebGL 使用我們的點(diǎn)數(shù)組和紋理坐標(biāo)數(shù)組來繪制三角形。這里的數(shù)字6意味著我們數(shù)組中的每 6 個(gè)數(shù)字定義一個(gè)三角形。每個(gè)三角形都有 3 個(gè)角,每個(gè)角(或頂點(diǎn))都有一個(gè) x 和 y 坐標(biāo)。
使用 GPU 繪制圖像需要學(xué)習(xí)許多不同的東西,我發(fā)現(xiàn)這存在一個(gè)陡峭的學(xué)習(xí)曲線,但是一旦了解著色器的實(shí)際作用、紋理是什么、如何為著色器提供一些數(shù)字列表以及它們?nèi)绾谓M合在一起,它就開始變得有意義了,并且我意識(shí)到這一切有多么強(qiáng)大。
我希望你已經(jīng)能夠看到一些簡單和強(qiáng)大的功能。我知道 WebGL API 可能非常冗長,而且我仍然不能完全確定每個(gè)函數(shù)的作用,這對(duì)我來說絕對(duì)是一種新的編程范式,因?yàn)?GPU 與 CPU 如此不同,但這就是它如此令人興奮的原因。
原文鏈接:http://www.bimant.com/blog/webgl-shader-crash-course/
薦12個(gè)曾經(jīng)讓我眼界大開的網(wǎng)站,每一個(gè)都像發(fā)現(xiàn)了新大陸!要么超級(jí)好玩,要么超級(jí)實(shí)用(能幫你省錢的那種)!相信總有一個(gè)你看了就會(huì)立馬愛上!
1:正版中國 —— 以較低的價(jià)格買到正版軟件
網(wǎng)址:https://getitfree.cn/
正版中國是一個(gè)分享正版軟件限時(shí)免費(fèi)信息的網(wǎng)站。像常見的Office軟件給的折扣力度都很大,有時(shí)候直接到了2折,真的能省不少錢!
從網(wǎng)站的分類信息中我們可以看到,幾乎包含了不同系統(tǒng)的軟件產(chǎn)品,比如PC、Mac、iOS、Android、UMP……
2:考拉新媒體導(dǎo)航 —— 新媒體人必備的好網(wǎng)站!
網(wǎng)址:https://www.kaolamedia.com/
作為新媒體人,無論是做內(nèi)容運(yùn)營、活動(dòng)運(yùn)營,還是用戶運(yùn)營,工作中經(jīng)常需要用到不少網(wǎng)站。
前幾天,我發(fā)現(xiàn)了一個(gè)超贊的新媒體導(dǎo)航網(wǎng)站,里面搜集了100+個(gè)新媒體人常用的效率工具!!
涵蓋:配圖網(wǎng)站、自媒體網(wǎng)站、公眾號(hào)輔助工具、數(shù)據(jù)分析工具、裂變工具、社群運(yùn)營工具、表單工具、創(chuàng)意H5工具、小程序工具、視頻剪輯、效率管理等…
簡直太用心了!我覺得自己可以清理一波收藏夾了……
3:美麗化學(xué) —— 可能會(huì)讓你瘋狂地愛上化學(xué)
網(wǎng)址:http://www.envisioningchemistry.cn
說到化學(xué),很多人的反應(yīng)就是充滿了危險(xiǎn),或者是難聞的氣味。但也有很多人就是喜歡化學(xué)反應(yīng)的奇妙,甚至還專門建立了一個(gè)網(wǎng)站,從微觀的角度展示神奇的化學(xué)世界。
比如這個(gè)硝酸鉀和氯化銨的結(jié)晶過程 ,在微觀世界實(shí)在是太美了!
還有初中學(xué)化學(xué)時(shí)非常熟悉的沉淀反應(yīng):
還有這個(gè)雙氧水的催化反應(yīng)實(shí)驗(yàn):
【美麗化學(xué)】的項(xiàng)目發(fā)起人是梁琰,本碩畢業(yè)于清華大學(xué)化學(xué)系,他曾在一席的舞臺(tái)上分享過自己這個(gè)項(xiàng)目背后的故事,大家感興趣的話也可以去看一看。
一席 | 演講
4:Office-converter —— 可能是最好用的在線格式轉(zhuǎn)換網(wǎng)站
網(wǎng)址:https://cn.office-converter.com/說到格式轉(zhuǎn)換,相信很多人都是頭疼不已,比如PDF轉(zhuǎn)Word?flv格式的視頻如何轉(zhuǎn)換成mp4格式?使用這個(gè)萬能的在線格式轉(zhuǎn)換網(wǎng)站就好啦~
無須下載專門的軟件,在線就可以免費(fèi)轉(zhuǎn)換各種類型的文件格式,包括:文檔、視頻、音頻、圖片、電子書等。
5:AlteredQualia —— 各種好玩的神器~
網(wǎng)址:https://alteredqualia.com/
這是一位WebGL大神的個(gè)人主頁,里面有各種好玩的工具。
什么是WebGL?簡單來說,就是在無需安裝任何硬件的情況下,我們僅通過瀏覽器就能查看各種3D影像的技術(shù)。
部分截圖
每一個(gè)都非常有意思。
比如下面的星球放射線工具 。截下來作為PPT的背景圖片也是非常好的素材
https://alteredqualia.com/three/examples/lines_sphere_gl.html
又比如這個(gè)會(huì)隨著鼠標(biāo)移動(dòng)同步轉(zhuǎn)動(dòng)眼球的愛因斯坦
https://alteredqualia.com/xg/examples/albert.html
還有這個(gè)有點(diǎn)魔性的江南style舞蹈
https://alteredqualia.com/three/examples/webgl_psy.html
這里就只為大家展示這3個(gè)小工具,如果你喜歡,不妨把大神的每個(gè)WebGL工具都玩一遍~
6:求字體網(wǎng) —— 輕松識(shí)別不認(rèn)識(shí)的字體
網(wǎng)址:http://new.qiuziti.com/
遇到喜歡但又不認(rèn)識(shí)的字體怎么辦?別著急,給文字拍張照片,或截個(gè)圖,上傳到【求字體網(wǎng)】,就可以識(shí)別出來了
比如這是@這是三金 做的一張讀書筆記PPT
我想知道標(biāo)題用了什么字體,就可以這么做:
Step 1:截取標(biāo)題字體部分
頁面太復(fù)雜的話,網(wǎng)站識(shí)別效果較差,所以我們只截取標(biāo)題區(qū)域。
Step 2:上傳圖片
Step 3:選中清晰且有特點(diǎn)的單字
根據(jù)識(shí)別結(jié)果,發(fā)現(xiàn)「方正喵嗚體」最符合。另外,發(fā)現(xiàn)沒?網(wǎng)站還貼心地給出了字體的商用情況以及官方下載鏈接,可以說很貼心了
類似網(wǎng)站:識(shí)字體網(wǎng)
7:51PPT模板 —— 集結(jié)各路PPT大神的模板作品
網(wǎng)址:http://www.51pptmoban.com/ppt/
性質(zhì):全部免費(fèi);質(zhì)量高
這個(gè)網(wǎng)站是由個(gè)人運(yùn)營的,站長會(huì)向每個(gè)PPT作品的原作者去申請(qǐng)授權(quán),然后整理發(fā)布。該網(wǎng)站不僅僅提供免費(fèi)的PPT模板,還有很多PPT教程、PPT圖表等資源!
下載方法:
? 選擇你想要的模板
? 往下拉,找到「下載地址」按鈕
? 最后點(diǎn)擊「本地下載」。這樣模板就下好了,全程無須注冊(cè)~
來看一下模板的質(zhì)量:
8:OfficePlus —— 微軟官方出品的免費(fèi)模板下載網(wǎng)站
網(wǎng)址:http://www.officeplus.cn/Template/Home.shtml性質(zhì):全部免費(fèi);質(zhì)量高這是微軟Office官方網(wǎng)站,這個(gè)網(wǎng)站吧不光有PPT模板,還有Word、Excel模板,甚至還有很多高質(zhì)量的圖片。
里面的模板質(zhì)量有多高呢?我放兩份給大家看看:
直接秒殺很多付費(fèi)模板呀!這樣的PPT模板OfficePlus上還有很多。
另外呢,Office既然作為微軟自家孩子,自然有特殊福利。假如你的軟件為Office365,那么在新建PPT的時(shí)候,直接搜索關(guān)鍵詞,比如 “ 報(bào)告 ”,是可以直接搜索到OfficePLUS上的模板。
9:OpenStax CNX —— 免費(fèi)分享教科書的網(wǎng)站
網(wǎng)址:https://cnx.org/
看到滿屏的英文是不是慌啦?其實(shí)只要利用網(wǎng)頁的翻譯功能翻成中文就好。
這是萊斯大學(xué)(Rice University) 的巴拉紐克博士( Dr. Richard Baraniuk)在1999年創(chuàng)辦的一個(gè)網(wǎng)站,分享的書籍涉及設(shè)計(jì)、藝術(shù)、商業(yè)、數(shù)學(xué)計(jì)算等領(lǐng)域。
所有書籍會(huì)提供詳細(xì)的目錄 ↓
下載的時(shí)候也無須注冊(cè),而且免費(fèi)。可以說是非常良心了。
所以,如果你想找國外的教科書,不妨先來這個(gè)網(wǎng)站試試。
10:tunefind —— 快速找到熱門影視劇的BGM!
網(wǎng)址:https://www.tunefind.com/movie/zootopia-2016
不知道你有沒有這樣的經(jīng)歷?看到一部好看的片子(電影、美劇、綜藝都行)或者一款好玩的游戲,經(jīng)常被里面的BGM吸引,每次為了找到歌名,恨不得把整個(gè)百度掀翻。如果是英文歌曲,完全不用這么麻煩,因?yàn)橛袀€(gè)網(wǎng)站已經(jīng)幫你整理好了!它就是【tunefind】。
以最近非常火的《權(quán)利的游戲》(Game of Thrones)為例,看一下能搜到什么樣的結(jié)果?可以發(fā)現(xiàn),它會(huì)按照不同的Season進(jìn)行分類:
我們點(diǎn)進(jìn)最新的《Season 8 》看一下,發(fā)現(xiàn)它又細(xì)分為了不同的集數(shù),而且可以試聽。一旦知道歌名,歌曲下載的事情不就很輕松了嗎?
11:書格 —— 可能是最好用的古籍資料下載網(wǎng)站
網(wǎng)址:https://www.shuge.org/
【書格】是一個(gè)自由開放的在線古籍圖書館,致力于開放式分享、介紹、推薦有價(jià)值的古籍善本。分享內(nèi)容限定為公共版權(quán)領(lǐng)域的書籍(參照標(biāo)準(zhǔn)伯爾尼公約)。
該網(wǎng)站最大限度地還原書籍品貌、內(nèi)容,借此計(jì)劃讓大家自由、免費(fèi)地欣賞到那些難以現(xiàn)世的書籍,讓大家能從中感受到人類文明進(jìn)程。
使用方法:
12:Firefox Send —— 火狐出品的臨時(shí)網(wǎng)盤
網(wǎng)址:https://send.firefox.com/
這是火狐(Firefox)旗下的一個(gè)臨時(shí)網(wǎng)盤,Send 允許上傳和加密很大的文件(登陸后最多 可上傳2.5GB)來分享到網(wǎng)上。如果你想給同事或朋友分享什么資料,但是又希望分享完后不留在自己電腦或網(wǎng)盤上,就可以試試這個(gè)網(wǎng)站。
以 Send 創(chuàng)建的每個(gè)鏈接將在限定下載次數(shù)(最多100次)或限定時(shí)間內(nèi)(最多7天) 后過期,有種「閱后即焚」的味道。
輪事件:滾輪
滾動(dòng)(卷動(dòng))事件:滾輪、拖拽滾動(dòng)條、鍵盤方向鍵
<script type="text/javascript">
//滾輪事件:滾輪
//卷動(dòng)事件:滾輪、拖拽滾動(dòng)條、鍵盤?鍵
//IE和Chrome
gunlun.onmousewheel=function(){
this.innerHTML +="IE和Chrome<br/>";
}
//Firefox
gunlun.addEventListener("DOMMouseScroll", function(){
this.innerHTML +="Firefox<br/>";
})
</script>
判斷滾輪方向
<script type="text/javascript">
//滾輪事件:滾輪
//卷動(dòng)事件:滾輪、拖拽滾動(dòng)條、鍵盤?鍵
//IE和Chrome
gunlun.onmousewheel=function(e){
var ev=e || window.event;
console.log(ev.wheelDelta);//判斷滾輪方向的
//上120
//下-120
this.innerHTML +="IE和Chrome<br/>";
}
//Firefox
gunlun.addEventListener("DOMMouseScroll",function(e){
var ev=e || window.event;
console.log(ev.detail);//滾輪方向
//上-3
//下3
this.innerHTML +="Firefox<br/>";
})
</script>
兼容性封裝
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。