399:我和她的世界末日游戲常見問題匯總 游戲出錯怎么解決?這里6399小編為大家帶來我和她的世界末日常見問題匯總,包括無法啟動、黑屏、報錯等,并把解決辦法告訴大家,希望對大家有用。
1、無法啟動游戲,“錯誤代碼83”。
可能是防火墻阻止了steam啟動游戲,請將游戲加入白名單或暫時關閉防火墻,到游戲安裝目錄下直接點擊package/Teow.exe來啟動游戲即可。
2、為什么QQ截圖會導致游戲對話快進?
因為AVG常見的按住CTRL鍵是快進功能哦,請更換QQ截圖的默認快捷鍵就可以啦~
3、游戲內STEAM界面無法打開?
這一點不影響游戲正常運行,不過我們正在加緊處理~
內置截圖快捷鍵F12,截圖保存在“我的文檔”\NVLCloud\Teow\screenshot目錄下。
和Steam截圖系統聯動功能會盡快加入的>_
4、游戲打開黑屏,或者出現“找不到renderer”的紅色報錯。
說明您的系統可能不支持WebGL,請參考幾種可能的解決方式:
(1)請嘗試升級系統或顯卡驅動,或禁用集成顯卡,只使用獨立顯卡。
(2)嘗試采用軟件渲染模式:
在STEAM里右鍵選游戲-屬性。
在“常規”一欄,下方有“設置啟動選項”按鈕,點擊后在彈出窗口中輸入:
--disable-gpu
再次啟動游戲,會出現黃色警告窗口,但游戲基本功能正常。
此方法可能導致游戲畫面效果變差,卡頓等。
(3)其他解決方式,我們目前沒有類似環境導致無法驗證。如果您愿意幫忙參與測試和驗證是否修復,同樣請聯系官方郵箱或QQ群。
非常抱歉造成了不好的游戲體驗,您也可以先行退款。如果想在BUG修復后繼續體驗游戲,過后我們會重新發送激活碼給您。
5、進入游戲后沒有BGM,開始播放語音后出現嗡嗡聲的情況.
部分機器默認聲音采樣率過高,請右鍵點擊WINDOWS右下角欄的喇叭圖標
選擇聲音-播放-
選擇揚聲器屬性-高級
切換默認格式設置為48000Hz
6、游戲無法存檔,或打開彈窗報“A Javascript error" ?
這個問題已經在11月6日的更新中修正,版本[2260317]。
如果仍然遇到類似問題,請參考條目7的解決方案,提供更多信息發送到 info@nvlsoft.com
7、游戲依然打不開/運行不正常?
(1)請先嘗試STEAM的“檢查游戲完整性”功能,確認游戲數據完全下載。
(2)如果游戲完整依然出現問題,請盡量提供所使用的電腦硬件、操作系統版本、安裝的殺毒軟件、以及出現問題的截圖。
(3)提供更多信息的方法:
--info
再次啟動游戲,等約十秒會彈窗,點擊后關閉。
在游戲存檔目錄我的文檔\NVLCloud\Teow里面生成以下兩個文件:
nvlcloud-info.html和nvlcloud-info.png
以上就是6399小編帶來的全部攻略,更多內容請關注6399網站~
WebGL僅僅是一個光柵化引擎,它可以根據你的代碼繪制出點,線和三角形。
想要利用WebGL完成更復雜任務,取決于你能否提供合適的代碼,組合使用點,線和三角形代替實現。
本文將帶大家了解WebGL基礎的繪制流程,并結合前文圖片濾鏡(基礎濾鏡和lut濾鏡)和圖像卷積(模糊、銳化等)的應用,用WebGL來實現。
適合100%不了解WebGL的人閱讀,從0開始入門,直到實現一個圖像處理。
文章較長,我們先瀏覽下目錄:
WebGL基于HTML5 Canvas,所以我們需要使用Canvas作為載體。
<!DOCTYPE html>
<html>
<head>
<title>WebGL入門</title>
</head>
<body>
<input type="file" id="fileInput" name="選擇圖片"/>
<canvas id="canvas"></canvas>
<script type="text/javascript">
</script>
</body>
</html>
再通過getContext方法來獲取WebGL上下文。在上面的script標簽內加入下面的代碼:
const canvas=document.getElementById('canvas');
const gl=canvas.getContext('webgl');
gl.clearColor(1.0, 1.0, 0.0, 1.0); // 設置清空顏色緩沖區時的顏色
gl.clear(gl.COLOR_BUFFER_BIT); // 清空顏色緩沖區
直接運行上面的2行代碼清空,我們可以看到canvas被填充滿了黃色。
因為gl.clearColor中接受RGBA四個值的范圍是0~1,所以如果gl.clearColor(0.0, 0.0, 0.0, 1.0)就會填充黑色。
function drawPoint() {
// 1、獲取webgl
const canvas=document.getElementById('canvas');
const gl=canvas.getContext('webgl');
if (!gl) {
return;
}
// 2、清空屏幕
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 3、獲取著色器資源
const vertexSource=document.getElementById('vertex-shader-2d').innerHTML;
const fragmentSource=document.getElementById('fragment-shader-2d').innerHTML;
// 4、創建頂點著色器對象
let vertexShader=gl.createShader(gl.VERTEX_SHADER);
// 綁定資源
gl.shaderSource(vertexShader, vertexSource);
// 編譯著色器
gl.compileShader(vertexShader);
let fragmentShader=gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
// 5、創建一個著色器程序
let program=gl.createProgram();
// 把前面創建的兩個著色器對象加到著色器程序中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 連接著色器程序
gl.linkProgram(program);
// 使用程序
gl.useProgram(program);
// 6、畫點
gl.drawArrays(gl.POINTS, 0, 1);
}
運行drawPoint函數后我們會看到300x300的canvas被填充成黑色,中間有一個10x10的白色點。
現在來解讀下上面的代碼。
分為6步來看,前兩步獲取webgl并清空屏幕。
第3步獲取OpenGL Shading Language(GLSL)編寫的著色程序。
該語言運行于GPU,是類似于C或C++的強類型語言,它總是成對出現,每對方法中一個叫頂點著色器,另一個叫片斷著色器,組合起來稱作一個 program(著色程序)
頂點著色器的作用是計算頂點的位置,根據位置對點, 線和三角形在內的一些圖元進行光柵化處理。片斷著色器的作用是計算出當前繪制圖元中每個像素的顏色值。
可以利用JavaScript中創建字符串的方式創建GLSL字符串:
// 頂點著色器
const vertexSource=`
attribute vec4 a_position;
void main () {
// gl_Position為內置變量,表示當前點的位置
gl_Position=a_position;
// gl_Position為內置變量,表示當前點的大小,為浮點類型
gl_PointSize=10.0;
}
`;
// 片段著色器
const fragmentSource=`
// 設置浮點數精度
precision mediump float;
void main () {
// vec4是表示四維向量,這里用來表示RGBA的值[0~1],均為浮點數
gl_FragColor=vec4(1.0, 0.5, 1.0, 1.0);
}
`;
或者跟本文的例子一樣,將它們放在非JavaScript類型的標簽中:
<!-- 頂點著色器 -->
<script type='x-shader/x-vertex' id='vertex-shader-2d'>
attribute vec4 a_position;
void main () {
// gl_Position為內置變量,表示當前點的位置
gl_Position=a_position;
// gl_Position為內置變量,表示當前點的大小,為浮點類型,如果賦值是整數類報錯
gl_PointSize=10.0;
}
</script>
<!-- 片段著色器 -->
<script type='x-shader/x-fragment' id='fragment-shader-2d'>
// 設置浮點數精度
precision mediump float;
void main () {
// vec4是表示四維向量,這里用來表示RGBA的值[0~1],均為浮點數,如為整數則錯
gl_FragColor=vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
這里a_position屬性的數據類型是vec4,vec4是一個有四個浮點數據的數據類型。
GLSL中命名約定:
獲取到著色器資源后,接著創建著色器,綁定資源并編譯,然后創建著色程序,把編譯好的2個著色器加進來,再連接和使用該著色程序。
到這一步我們的著色程序就初始化完畢。
最后就是繪制drawArrays。
由于drawArrays之前的步驟應用頻繁,下面我們把它們封裝起來。
著色器都是成對出現的,比如本例中的vertexShader和fragmentShader。
獲取著色器資源source后,根據type創建不同的著色器,vertexShader的type是gl.VERTEX_SHADER,fragmentShader的type是gl.FRAGMENT_SHADER。
然后綁定并編譯。
function createShader(gl, type, source) {
const shader=gl.createShader(type); // 創建 shader 對象
gl.shaderSource(shader, source); // 往 shader 中傳入源代碼
gl.compileShader(shader); // 編譯 shader
// 是否編譯成功
const success=gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
創建著色程序,把著色器加進來,連接程序。
function createProgram(gl, vertexShader, fragmentShader) {
const program=gl.createProgram(); // 創建 program 對象
gl.attachShader(program, vertexShader); // 往 program 對象中傳入 WebGLShader 對象
gl.attachShader(program, fragmentShader);
gl.linkProgram(program); // 鏈接 program
// 是否鏈接成功
const success=gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
然后通過著色器script標簽的id,創建連接好的著色程序:
function createProgramFromScripts (gl, vertexShaderScriptId, fragmentShaderScriptId) {
const vertexSource=document.getElementById(vertexShaderScriptId).innerHTML;
const fragmentSource=document.getElementById(fragmentShaderScriptId).innerHTML;
const vertexShader=createShader(gl, gl.VERTEX_SHADER, vertexSource); // 創建頂點著色器
const fragmentShader=createShader(gl, gl.FRAGMENT_SHADER, fragmentSource); // 創建片元著色器
const program=createProgram(gl, vertexShader, fragmentShader); // 創建 WebGLProgram 程序
return program;
}
封裝好之后,上面的drawPoint函數就可以優化成下面這樣:
// 1、獲取webgl
const canvas=document.getElementById('canvas');
const gl=canvas.getContext('webgl');
if (!gl) {
return;
}
// 2、清空屏幕
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 3、創建連接好的著色程序
const program=createProgramFromScripts(gl, 'vertex-shader-2d', 'fragment-shader-2d');
// 4、使用上面的著色程序
gl.useProgram(program);
// 5、畫點
gl.drawArrays(gl.POINTS, 0, 1);
上面我們畫了一個點,現在畫多個點。
比如下面的3個點:
const points=[
0, 0.0,
0.5, 0.0,
0.0, 0.5
];
需要把這3個點的數據傳給webgl:
// 創建一個buffer,用來放3個點在裁剪空間的坐標
const buffer=gl.createBuffer();
// buffer和ARRAY_BUFFER綁定(可以理解成ARRAY_BUFFER=buffer)
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);
接著獲取shader中a_position的地址并做一些配置:
const positionAttributeLocation=gl.getAttribLocation(program, "a_position"); // 獲取shader中a_position的地址
gl.enableVertexAttribArray(positionAttributeLocation); // 開啟attribute
// 告訴attribute如何從positionBuffer(ARRAY_BUFFER)中讀取數據
gl.vertexAttribPointer(
positionAttributeLocation, // 屬性值a_position的位置
2, // 每次迭代運行提取兩個單位數據
gl.FLOAT, // 每個單位的數據類型是32位浮點型
false, // 不需要標準化
0, // 用符合單位類型和單位個數的大小
0, // 從緩沖起始位置開始讀取
);
最后繪制3個點:
gl.drawArrays(gl.POINTS, 0, 3); // 繪制3個點
把上面的繪制3個點改一下,可以繪制成三角形:
gl.drawArrays(gl.TRIANGLES, 0, 3); // 繪制三角形
因為圖元類型primitiveType為三角形gl.TRIANGLES, 頂點著色器每運行三次WebGL將會根據三個gl_Position值繪制一個三角形。
上面我們用到了buffer和attribute,那它們是干什么的呢?
其實,緩沖操作是GPU獲取頂點數據的一種方式。
gl.createBuffer創建一個緩沖;gl.bindBuffer是設置緩沖為當前使用緩沖; gl.bufferData將數據拷貝到緩沖,這個操作一般在初始化完成。一旦數據存到緩沖中,還需要告訴WebGL怎么從緩沖中提取數據傳給頂點著色器的屬性。
首先,我們要獲取WebGL給屬性分配的地址,這一步一般在初始化時完成。
// 詢問頂點數據應該放在哪里
const positionAttributeLocation=gl.getAttribLocation(program, 'a_position');
繪制前還需要發出3個命令。
1、告訴WebGL我們想從緩沖中提供數據。
gl.enableVertexAttribArray(location);
2、將緩沖綁定到 ARRAY_BUFFER 綁定點,它是WebGL內部的一個全局變量。
gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer);
3、告訴WebGL從 ARRAY_BUFFER 當前綁定點的緩沖獲取數據。
gl.vertexAttribPointer(
location,
numComponents,
typeOfData,
normalizeFlag,
strideToNextPieceOfData,
offsetIntoBuffer);
numComponents: 每個頂點有幾個單位的數據(1 - 4)
typeOfData: 單位數據類型是什么(BYTE, FLOAT, INT, UNSIGNED_SHORT, 等等…)
normalizeFlag: 標準化
strideToNextPieceOfData: 從一個數據到下一個數據要跳過多少位
offsetIntoBuffer: 數據在緩沖的什么位置
如果每個類型的數據都用一個緩沖存儲,stride 和 offset 都是 0 。
stride 為 0 表示 “用符合單位類型和單位個數的大小”。 offset 為 0 表示從緩沖起始位置開始讀取。
它們用 0 以外的值會復雜得多,雖然這樣會取得一些性能能上的優勢, 但是一般情況下并不值得。
標準化標記(normalizeFlag)適用于所有非浮點型數據。如果傳遞false就解讀原數據類型。
上面的例子中,我們的頂點坐標都是裁剪空間坐標,比如:
const points=[
0, 0.0,
0.5, 0.0,
0.0, 0.5
];
裁剪空間的x范圍是[-1, 1],正方向向右,y的范圍也是[-1, 1],正方向向上。
對于描述二維空間中的物體,比起裁剪空間坐標你可能更希望使用屏幕像素坐標。
所以我們來改造一下頂點著色器:
<script type='x-shader/x-vertex' id='vertex-shader-2d'>
attribute vec2 a_position;
uniform vec2 u_resolution;
void main () {
// 像素坐標轉到 0.0 到 1.0
vec2 zeroToOne=a_position.xy / u_resolution;
// 0->1 轉到 0->2
vec2 zeroToTwo=zeroToOne * 2.0;
// 0->2 轉到 -1->+1 (即裁剪空間)
vec2 clipSpace=zeroToTwo - 1.0;
gl_Position=vec4(clipSpace, 0, 1);
}
</script>
然后我們用像素坐標表示新的3個點:
const points=[
0, 0,
100, 0,
0, 100
];
使用program后,我們需要獲取vertex-shader-2d中添加的全局變量u_resolution的位置,并設置分辨率:
const resolutionUniformLocation=gl.getUniformLocation(program, "u_resolution");
// 設置全局變量 分辨率
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
然后繪制三角形:
gl.drawArrays(gl.TRIANGLES, 0, 3);
這時,我們的坐標系原點在左下角,如果要像傳統二維API那樣原點在左上角,我們只需翻轉y軸:
// gl_Position=vec4(clipSpace, 0, 1);
gl_Position=vec4(clipSpace * vec2(1, -1), 0, 1); // 翻轉y軸
我們將通過繪制兩個三角形來繪制一個矩形,每個三角形有三個點,所以一共有6個點:
const points=[
100, 100,
200, 100,
100, 200,
200, 100,
100, 200,
200, 200
];
然后繪制時把次數改成6次:
// 繪制
gl.drawArrays(gl.TRIANGLES, 0, 6);
首先我們接著改造上面坐標轉換后的頂點著色器,增加a_texCoord和v_texCoord。
attribute vec2 a_texCoord;
...
varying vec2 v_texCoord;
void main() {
...
// 將紋理坐標傳給片斷著色器
// GPU會在點之間進行插值
v_texCoord=a_texCoord;
}
然后用片斷著色器尋找紋理上對應的顏色:
<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision mediump float;
// 紋理
uniform sampler2D u_image;
// 從頂點著色器傳入的紋理坐標
varying vec2 v_texCoord;
void main() {
// 在紋理上尋找對應顏色值
gl_FragColor=texture2D(u_image, v_texCoord);
}
</script>
點擊選擇圖片按鈕后,加載圖片,圖片加載完成后開始繪制圖片。
const inputElement=document.getElementById('fileInput');
const canvasElement=document.getElementById('canvas');
fileInput.addEventListener('change', async (e)=> {
const imgElement=document.getElementById('canvas');
const img=new Image();
img.src=URL.createObjectURL(e.target.files[0]);
img.onload=function () {
imgElement.width=img.width;
imgElement.height=img.height;
drawPic(img); // 繪制圖片
}
}, false);
繪制圖片我們在drawPic函數中進行,首先獲取gl并創建著色程序:
function drawPic(image) {
// 獲取webgl
const canvas=document.getElementById('canvas');
const gl=canvas.getContext('webgl');
if (!gl) {
return;
}
// 創建連接好的著色程序
const program=createProgramFromScripts(gl, 'vertex-shader-2d', 'fragment-shader-2d');
}
接著找2個頂點坐標位置(分別是矩形和紋理的坐標):
let positionLocation=gl.getAttribLocation(program, "a_position");
let texcoordLocation=gl.getAttribLocation(program, "a_texCoord");
再畫一個和圖片一樣大小的矩形,首先我們獲取畫矩形需要的6個點:
const x1=0;
const x2=image.width;
const y1=0;
const y2=image.height;
const points=[
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]
再和上文一樣,把點的數據傳給webgl,并設置讀取方式:
let positionBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);
// 開啟 position attribute
gl.enableVertexAttribArray(positionLocation);
// 告訴attribute如何從positionBuffer(ARRAY_BUFFER)中讀取數據
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
然后創建紋理:
// 創建紋理
let texture=gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
并對圖片渲染做設置,保證可以渲染任何尺寸的圖片:
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
然后把圖片加載到上面創建的紋理中:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
然后告訴webgl如何從裁剪空間轉換到像素空間:
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
然后對a_texCoord的地址并做一些配置。
渲染紋理時需要紋理坐標,而不是像素坐標,無論紋理是什么尺寸,紋理坐標范圍始終是 0.0 到 1.0:
gl.enableVertexAttribArray(texcoordLocation);
// 給矩形提供紋理坐標
let texCoordBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
接著清空屏幕并使用著色程序:
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
再設置全局變量分辨率:
let resolutionLocation=gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
到現在就可以畫矩形了(6個點畫2個三角形組成矩形):
gl.drawArrays(gl.TRIANGLES, 0, 6);
到這里圖片就出現了:
簡單說就是,我們畫了一個和圖片一樣大的矩形,創建了一個紋理并把圖片傳到紋理中,再把紋理貼到矩形上,這樣圖片就顯示出來了。
現在我們對圖片做一些簡單的像素操作。
比如紅藍換位,我們只需要改片段著色器:
gl_FragColor=texture2D(u_image, v_texCoord).bgra;
解析出顏色通道后,做加權算法,再重新設置顏色:
vec4 color=texture2D(u_image, v_texCoord);
float gray=0.2989 * color.r + 0.5870 * color.g + 0.1140*color.b;
gl_FragColor=vec4(gray, gray, gray, color.a);
更多改變像素顏色的風格算法,可以看看之前寫的《前端基礎濾鏡》。