|
12 | 12 |
|
13 | 13 | ## 实现思路 |
14 | 14 |
|
15 | | -甲实现了一个Render模块,该模块实现了初始化WebGL、渲染、Tonemap后处理的逻辑 |
| 15 | +甲实现了一个Render模块,该模块实现了渲染,包括初始化WebGL、渲染、Tonemap后处理这三个步骤 |
16 | 16 |
|
17 | 17 | 为了处理运行环境的差异,他的思路如下; |
18 | 18 |
|
@@ -155,26 +155,288 @@ tonemap for WebGL2 |
155 | 155 |
|
156 | 156 | ## 概述解决方案? |
157 | 157 |
|
158 | | -TODO continue |
| 158 | +分离PC端和移动端的渲染逻辑: |
| 159 | +从Render模块中提出两个模块:RenderInPC, RenderInMobile,分别对应PC端和移动端的渲染; |
| 160 | + |
| 161 | +因为初始化WebGL、渲染、Tonemap后处理这三个步骤相对独立,所以将其拆分成单独的模块 |
| 162 | + |
159 | 163 |
|
160 | 164 | ## 给出UML? |
| 165 | + |
| 166 | +TODO tu |
| 167 | + |
| 168 | +Render负责判断运行环境,调用对应的渲染模块来渲染 |
| 169 | + |
| 170 | +RenderInPC实现了PC端的渲染 |
| 171 | + |
| 172 | +RenderInMobile实现了移动端的渲染 |
| 173 | + |
| 174 | +InitWebGL2负责初始化WebGL2 |
| 175 | + |
| 176 | +DeferRender实现延迟渲染 |
| 177 | + |
| 178 | +TonemapForWebGL2使用WebGL2实现Tonemap后处理 |
| 179 | + |
| 180 | +InitWebGL1负责初始化WebGL1 |
| 181 | + |
| 182 | +ForwardRender实现前向渲染 |
| 183 | + |
| 184 | +TonemapForWebGL1使用WebGL1实现Tonemap后处理 |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | + |
161 | 190 | ## 结合UML图,描述如何具体地解决问题? |
| 191 | + |
| 192 | +- 现在甲负责RenderInPC和对应的三个模块,乙负责RenderInMobile和对应的另外三个模块,互相不影响 |
| 193 | + |
| 194 | + |
| 195 | + |
| 196 | + |
162 | 197 | ## 给出代码? |
163 | 198 |
|
164 | 199 |
|
165 | | -<!-- ## 请分析存在的问题? |
166 | | -## 提出改进方向? --> |
| 200 | +Client代码: |
| 201 | +```ts |
| 202 | +//假canvas |
| 203 | +let canvas = { |
| 204 | + getContext: (_) => 1 as any |
| 205 | +} |
| 206 | + |
| 207 | +//指定运行环境 |
| 208 | +globalThis.isPC = true |
| 209 | + |
| 210 | + |
| 211 | +let renderState = createState() |
| 212 | + |
| 213 | +renderState = render(renderState, canvas) |
| 214 | +``` |
| 215 | + |
| 216 | +Client代码跟之前一样 |
| 217 | + |
| 218 | +我们看下Renderd的createState相关代码: |
| 219 | +RenderStateType |
| 220 | +```ts |
| 221 | +export type renderInPCState = { |
| 222 | + gl: WebGL2RenderingContext | null |
| 223 | +} |
| 224 | + |
| 225 | +export type renderInMobileState = { |
| 226 | + gl: WebGL2RenderingContext | null |
| 227 | +} |
| 228 | + |
| 229 | +export type state = { |
| 230 | + renderInPC: renderInPCState, |
| 231 | + renderInMobile: renderInMobileState, |
| 232 | +} |
| 233 | +``` |
| 234 | +Render |
| 235 | +```ts |
| 236 | +export let createState = (): state => { |
| 237 | + return { |
| 238 | + renderInPC: { |
| 239 | + gl: null |
| 240 | + }, |
| 241 | + renderInMobile: { |
| 242 | + gl: null |
| 243 | + } |
| 244 | + } |
| 245 | +} |
| 246 | +``` |
| 247 | + |
| 248 | +state使用两个字段来分别保存两个运行环境的数据,互不干扰 |
| 249 | + |
| 250 | + |
| 251 | +我们看下Renderd的Render相关代码: |
| 252 | +```ts |
| 253 | +let _isPC = () => { |
| 254 | + return globalThis.isPC |
| 255 | +} |
| 256 | + |
| 257 | +export let render = (state: state, canvas) => { |
| 258 | + console.log(globalThis.isPC ? "is PC" : "is mobile") |
| 259 | + |
| 260 | + if (_isPC()) { |
| 261 | + state = renderInPC(state, canvas) |
| 262 | + } |
| 263 | + else { |
| 264 | + state = renderInMobile(state, canvas) |
| 265 | + } |
| 266 | + |
| 267 | + return state |
| 268 | +} |
| 269 | +``` |
| 270 | + |
| 271 | +render函数判断了运行环境,如果是PC端就调用RenderInPC模块来渲染;否则就调用RenderInMobile模块来渲染 |
| 272 | + |
| 273 | +我们看下RenderInPC代码 |
| 274 | +```ts |
| 275 | +export let render = (state: state, canvas) => { |
| 276 | + state = initWebGL2(state, canvas) |
| 277 | + state = deferRender(state) |
| 278 | + state = tonemap(state) |
| 279 | + |
| 280 | + return state |
| 281 | +} |
| 282 | +``` |
| 283 | + |
| 284 | +它依次调用三个模块来执行渲染 |
| 285 | + |
| 286 | +三个模块的相关代码如下: |
| 287 | +InitWebGL2 |
| 288 | +```ts |
| 289 | +export let initWebGL2 = (state: state, canvas) => { |
| 290 | + console.log("初始化WebGL2") |
| 291 | + |
| 292 | + return { |
| 293 | + ...state, |
| 294 | + renderInPC: { |
| 295 | + gl: canvas.getContext("webgl2") |
| 296 | + } |
| 297 | + } |
| 298 | +} |
| 299 | +``` |
| 300 | +DeferRender |
| 301 | +```ts |
| 302 | +export let deferRender = (state: state) => { |
| 303 | + let gl = getExnFromStrictNull(state.renderInPC.gl) |
| 304 | + |
| 305 | + console.log("延迟渲染") |
| 306 | + |
| 307 | + return state |
| 308 | +} |
| 309 | +``` |
| 310 | +TonemapForWebGL2 |
| 311 | +```ts |
| 312 | +export let tonemap = (state: state) => { |
| 313 | + let gl = getExnFromStrictNull(state.renderInPC.gl) |
| 314 | + |
| 315 | + console.log("tonemap for WebGL2") |
| 316 | + |
| 317 | + return state |
| 318 | +} |
| 319 | +``` |
| 320 | + |
| 321 | + |
| 322 | +我们看下RenderInMobile代码 |
| 323 | +```ts |
| 324 | +export let render = (state: state, canvas) => { |
| 325 | + state = initWebGL1(state, canvas) |
| 326 | + state = forwardRender(state) |
| 327 | + state = tonemap(state) |
| 328 | + |
| 329 | + return state |
| 330 | +} |
| 331 | +``` |
| 332 | + |
| 333 | +它依次调用另外三个模块来执行渲染 |
| 334 | + |
| 335 | +三个模块的相关代码如下: |
| 336 | +InitWebGL1 |
| 337 | +```ts |
| 338 | +export let initWebGL1 = (state: state, canvas) => { |
| 339 | + console.log("初始化WebGL1") |
| 340 | + |
| 341 | + return { |
| 342 | + ...state, |
| 343 | + renderInMobile: { |
| 344 | + gl: canvas.getContext("webgl1") |
| 345 | + } |
| 346 | + } |
| 347 | +} |
| 348 | +``` |
| 349 | +ForwardRender |
| 350 | +```ts |
| 351 | +export let forwardRender = (state: state) => { |
| 352 | + let gl = getExnFromStrictNull(state.renderInMobile.gl) |
| 353 | + |
| 354 | + console.log("前向渲染") |
| 355 | + |
| 356 | + return state |
| 357 | +} |
| 358 | +``` |
| 359 | +TonemapForWebGL1 |
| 360 | +```ts |
| 361 | +export let tonemap = (state: state) => { |
| 362 | + let gl = getExnFromStrictNull(state.renderInMobile.gl) |
| 363 | + |
| 364 | + console.log("tonemap for WebGL1") |
| 365 | + |
| 366 | + return state |
| 367 | +} |
| 368 | +``` |
| 369 | + |
| 370 | + |
| 371 | +下面,我们运行代码,运行结果如下: |
| 372 | +```text |
| 373 | +is PC |
| 374 | +初始化WebGL2 |
| 375 | +延迟渲染 |
| 376 | +tonemap for WebGL2 |
| 377 | +``` |
| 378 | + |
| 379 | +运行结果跟之前一样 |
| 380 | + |
| 381 | + |
| 382 | + |
| 383 | + |
167 | 384 | ## 提出问题 |
168 | 385 |
|
169 | 386 |
|
| 387 | +- 不能通过配置来指定渲染的步骤 |
| 388 | +现在是通过函数调用的方式来执行渲染的三个步骤。 |
| 389 | +如果不懂代码的策划人员想要自定义三个步骤的执行顺序,那就需要麻烦开发人员来修改代码,而不能够直接通过修改配置数据来自定义 |
| 390 | + |
| 391 | +- 多人开发渲染的不同步骤的模块时容易造成冲突 |
| 392 | +如果甲负责开发RenderInMobile中的InitWebGL1,乙负责开发RenderInMobile中的另外两个步骤模块,那么当他们把合并代码时容易出现代码冲突和Bug。 |
| 393 | +这是因为这三个步骤模块之间相互依赖,即另外两个步骤模块依赖InitWebGL1模块,所以甲负责的InitWebGL1模块如果有修改,会影响到乙负责的模块 |
| 394 | + |
| 395 | + |
| 396 | + |
| 397 | + |
170 | 398 | # [给出使用模式的改进方案] |
171 | 399 |
|
172 | 400 | ## 概述解决方案 |
| 401 | + |
| 402 | + |
| 403 | +通过下面的改进来实现通过配置来指定渲染的步骤: |
| 404 | +将RenderInPC和RenderInMobile模块改为两个渲染管道 |
| 405 | +将其中的三个步骤的模块改为独立的Job,按照JSON配置指定的执行顺序,对应的渲染管道中执行 |
| 406 | +<!-- 每个渲染管道有自己的数据,保存在自己的state中 --> |
| 407 | +<!-- Job可以读写所有的渲染管道的数据,不过不是直接 --> |
| 408 | +<!-- |
| 409 | +
|
| 410 | +不过只依赖于管道数据的类型 --> |
| 411 | + |
| 412 | +通过下面的改进来解决冲突的问题: |
| 413 | +因为渲染管道中的Job是独立的,相互之间不依赖,所以甲和乙同时开发不同的Job是不会相互影响的 |
| 414 | + |
| 415 | + |
| 416 | + |
173 | 417 | ## 给出UML? |
| 418 | + |
| 419 | +TODO tu |
| 420 | + |
| 421 | +TODO continue |
| 422 | + |
| 423 | + |
| 424 | +TODO |
| 425 | +每个渲染管道有自己的数据,保存在自己的state中 |
| 426 | +Job可以拿到每个渲染管道的数据,不过只依赖于管道数据的类型 |
| 427 | + |
| 428 | + |
| 429 | + |
| 430 | +TODO 现在增加一个开发同学乙,负责实现RenderInMobile管道的前向渲染、Tonemap的Job |
| 431 | +甲负责实现RenderInPC管道的所有Job,以及RenderInMobile管道的初始化WebGL1 |
| 432 | + |
174 | 433 | ## 结合UML图,描述如何具体地解决问题? |
175 | 434 | ## 给出代码? |
176 | 435 |
|
177 | 436 |
|
| 437 | +TODO |
| 438 | +这里需要实现的是能够合并甲、乙开发的同属于RenderInMobile管道的两个子管道。其中乙实现的两个Job应该在甲实现的Job之后执行,并且乙的Job需要读甲的子管道数据:WebGL1的上下文 |
| 439 | + |
178 | 440 |
|
179 | 441 | <!-- # 设计意图 |
180 | 442 |
|
|
0 commit comments